리액트- 사용자 경험개선 - 유틸함수 작성

정영찬·2022년 4월 25일
0

리액트

목록 보기
69/79
post-custom-banner

이전에 같은항목을 다시 클릭하면 로딩이 나타나지않게 기능을 개선하는 코드를 작성했는데, 이를 리팩토링하여 짧은 코드로 만들어보자.

asyncUtils.js 에서 createPromiseThunkById 라는 함수를 작성한다.


const defaultIdSelector = param => param;
export const createPromiseThunkById= (type, promiseCreator, idSelector = defaultIdSelector) => {
  const [SUCCESS, ERROR] = [`${type}_SUCCESS`, `${type}_ERROR`];

  const thunkCreator = (param) => async (dispatch) => {
    const id = idSelector(param);
    dispatch({ type, meta: id });
    try {
      const payload = await promiseCreator(param);
      dispatch({
        type: SUCCESS,
        payload,
        meta: id
      });
    } catch (e) {
      dispatch({
        type: ERROR,
        payload: e,
        error: true,
        meta: id
      });
    }
  };
  return thunkCreator;
}

※ 어차피 promiseCreator에서 getPostById로 쓸거면, 파라미터는 id 하나밖에 없는데, 왜 굳이 idSelector 같은걸 추가했지?

지금의 경우는 posts모듈에서 getPostById의 파라미터는 id 하나이지만, 파라미터가 복수 형태로 구성될 수도 있으며, 그럴 경우에는 어떤 파라미터가 id 값인지를 선택해야할 필요가 있기 때문에 idSelector로 작성한 것이다.

idSelector의 값을 id로 지정하고 dispath 항목에서 meta: id를 추가해주면 된다.

이제 모듈에서 적용시켜보자. getPost의 내용을 전부 삭제하고,

export const getPost = createPromiseThunkById(GET_POST, postsAPI.getPostById)

이렇게 한줄로만 바꿔도 잘 실행된다.

다음에는 getPostReduer를 리팩토링해보자. asyncUtils.js 에서 handleAsyncActions와 비슷한 형식의 handleAsyncActionsById를 작성한다.

차이점이 있다면 전에는 key값의 state를 업데이트 했지만, 이번에는 key 값의 id의 내용을 업데이트 해주어야한다.
id는 action의 meta값을 가져온다.

const id = action.meta;

예를 들어서 case type의 경우

case type:
        return {
          ...state,
          [key]: {
            ...state[key],
            [id]: reducerUtils.loading(keepData ? (state[key][id] && state[key][id].data) : null),
          }
        };

key 내부의 id 내용을 업데이트 하되. state[key][id]가 undefined일수 있기 때문에 && 연산자를 사용해서 해당 객체가 존재 할때에만 data를 참조하게 한 것이다.

export const handleAsyncActionsById = (type, key, keepData) => {
  const [SUCCESS, ERROR] = [`${type}_SUCCESS`, `${type}_ERROR`];
  return (state, action) => {
    const id = action.meta;
    switch (action.type) {
      case type:
        return {
          ...state,
          [key]: {
            ...state[key],
            [id]: reducerUtils.loading(keepData ? (state[key][id] && state[key][id].data) : null),
          }
        };
      case SUCCESS:
        return {
            ...state,
            [key]: {
              ...state[key],
              [id]: reducerUtils.success(action.payload)}
        };
      case ERROR:
        return {
          ...state,
          [key]: {
            ...state[key],
            [id]: reducerUtils.error(action.payload)}
      };
      default:
        return state;
    }
  };
};

이제 getPostReducer를 이 함수를 사용해서 한줄로 줄일수 있게 된다.

const getPostReducer = handleAsyncActionsById(GET_POST,'post', true)
profile
개발자 꿈나무
post-custom-banner

0개의 댓글