티스토리 뷰
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
'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
- React.memo
- clean code
- Vuex
- SOAP API
- v-for
- 문제풀이
- 알고리즘
- 프로그래머스
- redux-thunk
- Repository Pattern
- bundler
- reactrouter
- 백준
- webpack
- js
- error
- 상호평가
- Vue
- TypeScript
- python
- Transpiler
- React
- Vue.js
- Preloading
- redux
- SPA
- GraphQL
- programmers
- AxiosInterceptor
- 파이썬
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |