redux-thunk, redux-promise에 대해서는 아래 게시판에서 간략하게 살펴본 적 있습니다.
여기서는 조금 더 자세하게 살펴봅시다.
applyMiddleware, compose를 활용하여 enhancer 만들기 사용하기
커스텀 미들웨어
직접 미들웨어를 만들 수 있습니다.
const loggerMiddleware = (store) => (next) => (action) => {
console.log("middleware!!");
// next로 넘겨주지 않으면 리듀서로 액션을 진행하지 못합니다.
next(action);
};
export default loggerMiddleware;
import loggerMiddleware from "./lib/loggerMiddleware";
const store = createStore(rootReducer, applyMiddleware(loggerMiddleware));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
redux-logger
npm i redux-logger
import { createLogger } from "redux-logger";
const logger = createLogger();
const store = createStore(rootReducer, applyMiddleware(logger));
미들웨어를 활용하여 비동기 처리하기
action을 plain한 객체가 아닌 함수 형태로 만든 후에 실행한 결과를 plain 객체로 돌려주면 됩니다.
const thunkMiddleware = (store) => (next) => (action) => {
// action이 객체가 아닌 함수인 경우 비동기라 가정하고 처리하자.
if (typeof action === "function") {
// action 함수에 next, getState 함수를 넘겨서 활용할 수 있도록 하자
return action(next, store.getState);
}
next(action);
};
action 함수는 다음과 같습니다
// async action
const asyncLogin = (data) => {
return (next, getState) => {
// 요청 보내기
next(loginRequest(data));
try {
// 로그인 과정 2초
setTimeout(() => {
next(loginSuccess({ id: 1, name: "darren", admin: true }));
}, 2000);
} catch (error) {
next(loginFailure(error));
}
};
};
const loginRequest = (data) => {
return {
type: LOG_IN_REQUEST,
data,
};
};
const loginSuccess = (data) => {
return {
type: LOG_IN_SUCCESS,
};
};
const loginFailure = (error) => {
return {
type: LOG_IN_FAILURE,
error,
};
};
redux-thunk
비동기 함수 형태의 액션을 디스패치할 수 있게 됩니다. 비동기를 처리하는 미들웨어에서 가장 자주 사용된다고 합니다.
npm i redux-thunk
import ReduxThunk from "redux-thunk";
const store = createStore(rootReducer, applyMiddleware(logger, ReduxThunk));
비동기로 처리해야 하는 데이터 페칭 관련 함수를 redux-thunk를 이용해서 사용해보겠습니다.
왜 이런 처리를 해야 할까요? 그야 각 컴포넌트 단에서 state관리를 해주는 것보다 전역 차원에서 데이터를 페칭하고 활용하는 편이 더욱 편리하기 때문입니다.
우선 다음과 같이 데이터를 페칭해오는 함수가 존재한다면,
import axios from "axios";
export const getPost = (id) =>
axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`);
export const getUsers = (id) =>
axios.get(`https://jsonplaceholder.typicode.com/posts/users`);
이를 처리하는 reducer와 action들은 다음과 같이 작성할 수 있습니다.
saga에서도 마찬가지로 하나의 요청마다 3개의 액션을 생성해줘야 합니다.
REQUEST (요청 날림)
SUCCESS (요청 성공)
FAILURE (요청 실패)
로 나뉘로, 액션이 이 3개로 구성합니다.
import { handleActions } from "react-actions";
import * as api from "../lib/api";
// api 요청에 따른 액션 타입은 요청, 성공, 실패 3개로 나뉩니다.
const GET_POST = "sample/GET_POST";
const GET_POST_SUCCESS = "sample/GET_POST_SUCCESS";
const GET_POST_FAILURE = "sample/GET_POST_FAILURE";
// thunk 함수를 생성합니다.
export const getPost = (id) => async (dispatch) => {
dispatch({ type: GET_POST }); // 요청을 보냈습니다.
try {
const response = await api.getPost(id);
dispatch({ type: GET_POST_SUCCESS, payload: response.data });
} catch (e) {
dispatch({ type: GET_POST_FAILURE, payload: e, error: true });
throw e;
}
};
const initialState = {
loading: {
GET_POST: false,
},
post: null,
};
// handleActions을 이용해 reducer를 만듭니다.
const sample = handleActions(
{
[GET_POST]: (state) => ({
...state,
loading: {
...state.loading,
GET_POST: true,
},
}),
[GET_POST_SUCCESS]: (state, action) => ({
...state,
loading: {
...state.loading,
GET_POST: false,
},
post: action.payload,
}),
[GET_POST_FAILURE]: (state, action) => ({
...state,
loading: {
...state.loading,
GET_POST: false,
},
post: action.payload,
}),
},
initialState
);
export default sample;
이제 구체적인 컴포넌트에서 useSelect나 useDispatch를 이용하여 위 리듀서를 활용하여 데이터 페칭과 로딩을 진행하면 됩니다.
'State Management > ⚛ Redux' 카테고리의 다른 글
immer로 redux state 불변성 유지하기 (0) | 2021.05.21 |
---|---|
redux-saga를 통한 redux 비동기 처리 (1) : redux-saga 설치 및 구성 (0) | 2020.08.14 |
redux-action으로 액션, 리듀서를 간략히 작성하기 (0) | 2020.07.18 |
react-redux의 hook : useDispatch, useSelector (0) | 2020.06.27 |
applyMiddleware, compose를 활용하여 enhancer 만들기 사용하기 (0) | 2020.06.27 |