패스트캠퍼스 리덕스 미들웨어 강의시작
비동기 작업을 처리할 때 많이 사용하는 미들웨어다. 객체가 아닌 함수 형태의 액션을 디스패치할 수 있게 해준다.
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,
}),
};
해당 형식으로 객체에서 필요한 함수들을 정의해두고 필요할때 가져다 쓰는방식이다.
// 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 해준다.
강의에서 리팩토링 하는 부분이 많아서 조금 헷갈리지만
흐름정도는 이해되는 수준 더해보자