Redux를 이용한 상태관리 실습편 (미들웨어 함수 작성)

개발 요모조모·2022년 4월 11일
0

redux

목록 보기
4/6
post-thumbnail

저번 파트에서 다뤘던 redux-thunk를 이용해 비동기 요청을 다뤄보았다. 하지만 thunk함수와 로딩 상태를 리듀서에서 관리하려다 보니 코드가 길어져 가독성이 떨어진다. 그래서 반복되는 로직을 분리해 간편하게 작성하고자 한다.

먼저 유틸 함수 파일을 만들어 코드를 작성한다.


lib/createRequestThunk.js


export default function createRequestThunk(type, request) {
  const SUCCESS = `${type}_SUCCESS`;
  const FAILURE = `${type}_FAILURE`;
  return (params) => async (dispatch) => {
    dispatch({ type });
    try {
      const res = await request(params);
      dispatch({
        type: SUCCESS,
        payload: res.data,
      });
    } catch (e) {
      dispatch({
        type: FAILURE,
        payload: e,
        error: true,
      });
      throw e;
    }
  };
}

modules/ sample.js

createRequestThunk를 만들어 리듀서 안에서 간단하게 코드 작성을 할 수 있다. 타입(GET_POST,GET_USERS)과 요청(api.getPost,api.getUsers)을 인자로 넣어주어 위에 작성한 함수가 실행이 되고 성공, 실패 요청을 조회할 수 있게 해준다.

import { handleActions } from "redux-actions";
import * as api from "../lib/api";
import createRequestThunk from "../lib/createRequestThunk";

const GET_POST = "sample/GET_POST";
const GET_POST_SUCCESS = "sample/GET_POST_SUCCESS";
const GET_POST_FAILURE = "sample/GET_POST_FAILURE";

const GET_USERS = "sample/GET_USERS";
const GET_USERS_SUCCESS = "sample/GET_USERS_SUCCESS";
const GET_USERS_FAILURE = "sample/GET_USERS_FAILURE";


// createRequestThunk(type, request)
export const getPost = createRequestThunk(GET_POST, api.getPost);
export const getUsers = createRequestThunk(GET_USERS, api.getUsers);

다음으로는 리듀서 내부에서 관리된 로딩 상태를 따로 모듈을 생성해서 처리해주도록 한다.

modules/ loading.js

import { createAction, handleActions } from "redux-actions";

const START_LOADING = "loading/START_LOADING";
const FINISH_LOADING = "loading/FINISH_LOADING";

export const startLoading = createAction(
  START_LOADING,
  (requestType) => requestType
);

export const finishLoading = createAction(
  FINISH_LOADING,
  (requestType) => requestType
);

const initialState = {};

const loading = handleActions(
  {
    [START_LOADING]: (state, action) => ({
      ...state,
      [action.payload]: true,
    }),
    [FINISH_LOADING]: (state, action) => ({
      ...state,
      [action.payload]: false,
    }),
  },
  initialState
);

export default loading;

{
	type: 'loading/START_LOADING',
	payload: 'sample/GET_POST'
}

요청이 시작되면 위에 있는 액션이 디스패치 되고 sample/GET_POST 상태를 true로 설정해준다. 위에 필드가 없으면 새로 값을 설정해주고 이 요청이 끝나면 다음 액션을 디스패치 해야 한다.


{
	type: 'loading/FINISH_LOADING',
	payload: 'sample/GET_POST'
}

true인 상태를 false오 바꿔주고 이 모듈을 루트리듀서에 적용해준다. 그리고 마찬가지로 위에서 작성한 createRequestThunk에 loading모듈의 액션 생성 함수를 사용해준다.



containers/SampleContainer.js

connect함수 내부를 아래와 같이 변경해준다.


export default connect(
  ({ sample, loading }) => ({
    post: sample.post,
    users: sample.users,
    loadingPost: loading["sample/GET_POST"],
    loadingUsers: loading["sample/GET_USERS"],
    // loadingPost: sample.loading.GET_POST,
    // loadingUsers: sample.loading.GET_USERS,
  }),
  {
    getPost,
    getUsers,
  }
)(SampleContainer);

이제 sample 리듀서에서 로딩 중과 관련된 상태를 관리할 필요가 없기 때문에 성공 케이스만 빼고 지워주면 된다.


const sample = handleActions(
  {
    // [GET_POST]: (state) => ({
    //   ...state,
    //   loading: {
    //     ...state.loading,
    //     GET_POST: true,
    //   },
    // }),
    [GET_POST_SUCCESS]: (state, action) => ({
      ...state,
      loading: {
        ...state.loading,
        GET_USERS: false,
      },
      post: action.payload,
    }),
    // [GET_POST_FAILURE]: (state, action) => ({
    //   ...state,
    //   loading: {
    //     ...state.loading,
    //     GET_USERS: false,
    //   },
    // }),
    // [GET_USERS]: (state) => ({
    //   ...state,
    //   loading: {
    //     ...state.loading,
    //     GET_USERS: true,
    //   },
    // }),
    [GET_USERS_SUCCESS]: (state, action) => ({
      ...state,
      loading: {
        ...state.loading,
        GET_POST: false,
      },
      users: action.payload,
    }),
    // [GET_USERS_FAILURE]: (state, action) => ({
    //   ...state,
    //   loading: {
    //     ...state.loading,
    //     GET_POST: false,
    //   },
    // }),
  },
  initialState
);

만약 _FAILURE에 관련된 실패 상태를 처리하려면 컨데이너 컴포넌트에서 try/catch로 에러를 처리해주면 된다.


  useEffect(() => {
    const fn = async () => {
      try {
        await getPost(1);
        await getUsers(1);
      } catch (e) {
        console.log(e);
      }
    };
    fn();
  }, [getPost, getUsers]);



발췌:
https://github.com/reduxjs/redux-thunk
리액트를 다루는 기술, 김민준

profile
꾸준한 삽질과 가끔의 성취, 개발 그 사이에서

0개의 댓글