[React js] redux-middleware02

seokki kwon·2022년 9월 25일
0

패스트캠퍼스 리덕스 미들웨어 강의시작

redux-thunk

비동기 작업을 처리할 때 많이 사용하는 미들웨어다. 객체가 아닌 함수 형태의 액션을 디스패치할 수 있게 해준다.

Fake API

// n초 뒤에 끝나는 프로미스
const sleep = (n) => new Promise((resolve) => setTimeout(resolve, n));

// {id, title, body}
const posts = [
  { id: 1, title: "redux-middleware", body: "learn redux-middleware" },
  { id: 2, title: "react-typescript", body: "learn react-typescript" },
  { id: 3, title: "redux-saga", body: "learn redux-saga" },
];

// 0.5초 뒤에 posts 배열 반환
export const getPost = async () => {
  await sleep(500);
  return posts;
};

// 특정 post 반환
export const getPostById = async (id) => {
  await sleep(500);
  return posts.find((post) => post.id === id);
};

리팩토링

// modules/post.js
const initialState = {
  posts: reducerUtils.initial(),
  post: reducerUtils.initial(),
};

export default function post(state = initialState, action) {
  switch (action.type) {
    case GET_POSTS:
      return {
        ...state,
        posts: reducerUtils.loading(),
      };
    case GET_POSTS_SUCCESS:
      return {
        ...state,
        posts: reducerUtils.success(action.payload),
      };
    case GET_POSTS_ERROR:
      return {
        ...state,
        posts: reducerUtils.error(action.payload),
      };
    case GET_POST:
      return {
        ...state,
        post: reducerUtils.loading(),
      };
    case GET_POST_SUCCESS:
      return {
        ...state,
        post: reducerUtils.success(action.payload),
      };
    case GET_POST_ERROR:
      return {
        ...state,
        post: reducerUtils.error(action.payload),
      };
    default:
      return state;
  }
}

우선 loading,data,error 형태의 객체를 리턴하는 함수를 한곳에 만들어두고 해당 형식으로
리팩토링을 하여 코드를 줄일 수 있다.

// lib/asyncUtils.js
export const reducerUtils = {
  initial: (initData = null) => ({
    data: initData,
    loading: false,
    error: null,
  }),
  loading: (prevState = null) => ({
    data: prevState,
    loading: true,
    error: null,
  }),
  success: (data) => ({
    data,
    loading: false,
    error: null,
  }),
  error: (error) => ({
    data: null,
    loading: false,
    error,
  }),
};

해당 형식으로 객체에서 필요한 함수들을 정의해두고 필요할때 가져다 쓰는방식이다.

createPromiseThunk 함수

// lib/asyncUtils.js
export const createPromiseThunk = (type, promiseCreator) => {
  const [SUCCESS, ERROR] = [`${type}_SUCCESS`, `${type}_ERROR`];

  // FLUX standard 를 따르면 좋다
  /*
    payload 라고 명칭을 정하고
    error 발생시 error 값을 true로 설정
*/
  const thunkCreator = (param) => async (dispatch) => {
    dispatch({ type });
    try {
      const payload = await promiseCreator(param);
      dispatch({
        type: SUCCESS,
        payload,
      });
    } catch (e) {
      dispatch({ type: ERROR, payload: e, error: true });
    }
  };

  return thunkCreator;
};

getPosts, getPost Thunk 함수는 Promise함수, type 만 다를뿐 같은 형태를 지닌 thunk함수였다
공통되는 부분을 합쳐준뒤 type과 promise함수만 인자로 받아와서 thunk함수를 반환해주도록 리팩토링 하였다.

비동기로 데이터 받아오기

import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { getPosts } from "../modules/post";
import PostList from "../components/PostList";

export default function PostListContainer() {
  const { data, loading, error } = useSelector((state) => state.post.posts);
  const dispatch = useDispatch();
  useEffect(() => {
    dispatch(getPosts());
  }, [dispatch]);
  if (loading) return <div>로딩중...</div>;
  if (error) return <div>에러...</div>;
  if (data)
    return (
      <div>
        <PostList posts={data} />
      </div>
    );
}

getPosts() 함수를 dispatch 해준다.
강의에서 리팩토링 하는 부분이 많아서 조금 헷갈리지만
흐름정도는 이해되는 수준 더해보자

profile
웹 & 앱개발 기록

0개의 댓글