티스토리 뷰

Redux Toolkit (RTK)와 TypeScript를 활용하여 작성한 로직입니다.

Redux Thunk

Redux Store는 state의 변화에 따라 UI를 업데이트하기 위해서, 동기적으로 로직이 돌아간다.

Redux Store에서 비동기적인 로직을 처리할 수 없기 때문에, Middleware를 추가하여 비동기적으로 처리되는 로직을 담당하게 한다. Redux Thunk는 Redux의 Middleware 중 하나로서, store와 상호작용하여 비동기적인 로직을 사용할 수 있다.

 

이번 프로젝트에서는 Redux Toolkit을 사용하기 때문에, configureStore 메서드 내부에 Redux Thunk가 기본적으로 세팅되어 있다.

 

Create useSelector & useDispatch

우선 여러 컴포넌트에서 재사용성을 높이기 위해서 useSelector를 미리 제작해둔다.

// ../hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "../store/store";

export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

 그리고 return 데이터 타입을 미리 선언한다.

export interface ResponseDataType {
  coord: CoordType;
  weather: WeatherType[];
  base: string;
  main: MainType;
  visibility: number;
  wind: WindType;
  clouds: CloudsType;
  dt: number;
  sys: SysType;
  timezone: number;
  id: number;
  name: string;
  cod: number;
}

export interface StateType {
  loading: boolean;
  data: ResponseDataType | null;
  error: string;
}

export interface ActionType {
  payload: ResponseDataType;
  type: string;
  error: string;
}

Redux Thunk 코드 작성 (with Axios)

Redux Thunk는 Thunk함수를 createAsyncThunk 함수로 작성할 수 있다.

 

이번 코드에서는 날씨 정보를 제공해주는 API를 활용하어 Thunk함수를 작성하였다.

interface weatherApiArgs {
	lat: number;
    lon: number;
}

export const getWeather = createAsyncThunk<
  ResponseDataType,
  weatherApiArgs,
  { rejectValue: ErrorMessage }
>("weather/getWeatherData", async ({ lat, lon }: weatherApiArgs, thunkApi) => {
  const { data, status }: { data: ResponseDataType; status: number } = await axios.get(
    `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${API_KEY}`,
  );

  if (status !== 200) {
    return thunkApi.rejectWithValue({
      message: "Failed to fetch todos.",
    });
  }

  return data;
});

여기서 주의 깊게 봐야 할 부분이 createAsyncThunk 부분의 타입을 설정하는 부분이다.

3가지의 타입을 설정해주어야 하는데, 타입의 종류는 다음과 같다.

 

첫 번째 타입은 return 하는 데이터의 타입을 선언해준다.

이번 비동기 로직으로 얻고자 하는 날씨 데이터의 타입을 선언해주었습니다.

 

두 번째 타입은 비동기 로직에 사용될 인자에 대한 타입을 선언해준다.

이번 비동기 로직에서는 내부에 위도, 경도 데이터를 사용하기 때문에 interface로 인자들의 타입을 선언하여 Thunk 함수에 반영해주었습니다.

 

세 번째 타입은 type 객체가 들어가는 부분이다.

type 객체는 {dispatch?, state?, extra?, rejectValue?}로 구성되어있다.

extra 부분은 요청 메서드에 필요한 static 데이터가 포함될 수 있고, 'rejectedValue에는 에러가 발생했을 때 return 할 type을 정의해둘 수 있다.

 

비동기 로직을 작성하면서 인자로 thunkApi라는 객체를 인자로 전달하는데, rejectWithValue와 같은 field를 포함하고 있다.

 

Slice 코드 작성

Slice 내부에 thunk를 추가하는 코드를 작성해준다.

Thunk로 작성한 코드는 extraReducer에 action을 추가해준다.

// ../store/weatherSlice.ts
const weatherSlice = createSlice({
  name: "weather",
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase(getWeather.pending, (state) => {
      state.loading = true;
    });
    builder.addCase(getWeather.fulfilled, (state, { payload }) => {
      state.loading = false;
      state.data = payload;
    });
    builder.addCase(getWeather.rejected, (state, { payload }) => {
      if (payload) state.error = payload.message;
      state.loading = false;
    });
  },
});

 

Component에 Thunk를 사용

작성한 Thunk 함수를 사용하는 컴포넌트에 데이터를 가져오는 코드를 작성한다.

// weatherComponent.tsx
const dispatch = useAppDispatch();
  const { data, error, loading } = useAppSelector((state: RootState) => state.weather);

  const lat = 35;
  const lon = 139;

  useEffect(() => {
    void dispatch(getWeather({ lat: lat, lon: lon }));
  }, []);

 

아직 미숙한 TypeScript와 함께 작성하려고 해서 Redux-Thunk 코드를 작성하는 방법을 이해하기 더 힘들었던 것 같다.

 

Thunk와 비슷한 미들웨어로 Redux-Saga가 존재하는데

현재 만들고 있는 날씨 앱을 어느 정도 만들어두고, Redux-Thunk를 Redux-Saga로직으로 리팩터링 해보려고 한다.

 

## Reference

 

https://www.newline.co/@bespoyasov/how-to-use-thunks-with-redux-toolkit-and-typescript--1e65fc64

 

www.newline.co

 

How to use createAsyncThunk with Typescript? How to set types for the `pending` and `rejected` payloads?

Right now I've got these actions that I use for an upload thunk's lifecycle. type UPLOAD_START = PayloadAction<void> type UPLOAD_SUCCESS = PayloadAction<{ src: string, sizeKb: number }&g...

stackoverflow.com

 

'Vue&React' 카테고리의 다른 글

[React] Image Preloading  (0) 2022.07.15
[React] React.FC<>  (0) 2022.07.11
[React] Modal with Portal  (0) 2022.05.10
Context API  (0) 2022.04.27
Vuex - (2)  (0) 2022.04.26
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함