시리즈에어비앤비 v2

redux toolkit createAsyncThunk

ansupati님의 프로필 사진

ansupati

공식

2020.09.14. 23:111,514 읽음

redux toolkit createAsyncThunk



지난번에 swr 을 이용해서 데이터를 fetch 를 했는데 다시 생각이 바뀌었다.

이것보다는 지금 쓰고 있는 redux toolkit 을 더 잘 활용하는게 낫다.

일단 가장 큰 이유는 역시 바로 디버깅이다.

프론트단에서 데이터를 불러와서 그것을 랜더링 하는 이 과정은 굉장히 중요한 과정이고

이 과정을 꼼꼼하게 확인하기가 쉬워야 한다.

그래서 보통 redux  에서는 redux middleware를 사용해서  thunk나 redux saga를 붙여서 사용한다. 

특히 redux saga 에서는 데이터를 요청하는 dispatch, 성공후 해야하는 dispatch, 실패했을때 해야하는 dispatch 세가지 동작으로 나뉘어져 있고 이 세가지 동작으로 나눠서 처리하는게 data fetching의 일반적인 방식으로 보인다. 하지만 이 redux saga 에서 항상 이 세가지동작을 셋팅하는게 되게 번거롭고 코드가 너무 길어진다는 단점이 있어서 그동안 좀 꺼려왔다.

굳이 이렇게 너저분하게 써야되?

그런데 하다보면 이게 필요한것 같다. redux dev tool 에 항상 이 세가지 과정이 찍히다보니까 디버깅 하기가 편하다.


export const fetchRoomsByPlace = createAsyncThunk( 'home/fetchRoomsByPlace', async (params : any, { getState, requestId } : any) => { const { currentRequestId = '', loading } = getState().homeReducer; if (!_.isEqual(loading, LOADING_TYPE_ENUM.PENDING || !_.isEqual(requestId, currentRequestId))) { return null; } const { place = DEFAULT_PLACE } = params; const response = await axios.get(`${API_URL}?place=${place}`); return response.data; }, ); const homeSlice = createSlice({ name: 'homeReducer', initialState, reducers: { setDataFromIndex(state, action) { state.loading = action?.payload?.loading ?? false; }, }, extraReducers: { [fetchRoomsByPlace.pending]: (state, action) => { if (_.isEqual(state.loading, LOADING_TYPE_ENUM.IDLE)) { state.loading = LOADING_TYPE_ENUM.PENDING; state.currentRequestId = action.meta.requestId; } }, [fetchRoomsByPlace.fulfilled]: (state, action) => { if (_.isEqual(state.loading, LOADING_TYPE_ENUM.PENDING) && _.isEqual(state.currentRequestId, action.meta.requestId)) { state.loading = LOADING_TYPE_ENUM.IDLE; state.rooms = action.payload?.rooms ?? []; state.currentRequestId = undefined; } }, [fetchRoomsByPlace.rejected]: (state, action) => { if (_.isEqual(state.loading, LOADING_TYPE_ENUM.PENDING && _.isEqual(state.currentRequestId, action.meta.requestId))) { state.loading = LOADING_TYPE_ENUM.IDLE; state.error = action.error; state.currentRequestId = undefined; } }, }, });

필요한 셋팅은 사실상 이게 끝이다. 원래 redux saga 에서는 이거보다 훨씬 더 많은 셋팅이 필요하지만 redux toolkit 에서는 굉장히 편하게 간소화된 버전으로 쓸수가 있다. createAsyncThunk 함수에다가 데이터 fetching 하는 로직을 넣어주고. 그런데 저기서 보면 이 redux toolkit 에서는 requestId 라는걸 제공해준다.

통신할때 requestId가 생성이 되고 똑같은 requestId 일때는 통신을 하지 않고 그냥 리턴을 해줄수도 있다.

결국 이런식으로 조건을 걸어줄수 있다는건 내가 원할때만 통신을 할수 있도록 커스터마이징이 얼마든지 가능하다는거고 이부분은 상당히 중요하다.

그리고 extraReducer 부분에 pending, fulfilled, rejected  이 세가지 상황에 대해서 어떤 state를 바꿔줄것인지 정하면 된다.

그리고 컴포넌트 단에서는

const { rooms, loading } = useSelector((state: rootState) => state.homeReducer); const dispatch = useDispatch(); useEffect(() => { dispatch(fetchRoomsByPlace({ place: DEFAULT_PLACE })); }, []);

useSelector 로 상태값 불러오고 dispatch를 통해 한번만 딱 만들어놓은 함수 호출해주면 그 함수가 성공했으때 실패했을때 pending 일때 각각의 해당하는 동작을 다 셋팅해줬으니 알아서 돌아간다.

그리고 redux dev tool에서 그 과정을 다 살펴볼수도 있다. 이런식으로 redux toolkit 에서 디폴트로 제공하는 thunk 미들웨어를 활용해서 여러가지를 할수가 있다.

swr은 일단 지금은 굳이 쓰지 않기로 그냥..

로딩중