Redux-thunk란?
redux-thunk는 리덕스에서 비동기 작업을 처리 할 때 가장 많이 사용하는 미들웨어이다.
redux-thunk를 사용하면 액션 객체가 아닌 함수를 디스패치 할 수 있다.
함수를 디스패치 할 때 에는 해당 함수에서 dispatch와 getState를 파라미터로 받아와야 한다. 이 함수를 만들어주는 함수를 우리는 thunk라고 부른다.
const getComments = () => (dispatch, getState) => {
// 이 안에서는 액션을 dispatch 할 수도 있고
// getState를 사용하여 현재 상태도 조회 할 수 있습니다.
const id = getState().post.activeId;
// 요청이 시작했음을 알리는 액션
dispatch({ type: 'GET_COMMENTS' });
// 댓글을 조회하는 프로미스를 반환하는 getComments 가 있다고 가정해봅시다.
api
.getComments(id) // 요청을 하고
.then(comments => dispatch({ type: 'GET_COMMENTS_SUCCESS', id, comments })) // 성공시
.catch(e => dispatch({ type: 'GET_COMMENTS_ERROR', error: e })); // 실패시
};
const getComments = () => (dispatch, getState) => {
// 이 안에서는 액션을 dispatch 할 수도 있고
// getState를 사용하여 현재 상태도 조회 할 수 있습니다.
const id = getState().post.activeId;
// 요청이 시작했음을 알리는 액션
dispatch({ type: 'GET_COMMENTS' });
// 댓글을 조회하는 프로미스를 반환하는 getComments 가 있다고 가정해봅시다.
api
.getComments(id) // 요청을 하고
.then(comments => dispatch({ type: 'GET_COMMENTS_SUCCESS', id, comments })) // 성공시
.catch(e => dispatch({ type: 'GET_COMMENTS_ERROR', error: e })); // 실패시
};
이제 실제로 한번 구현해보자!
$ yarn add redux-thunk
import React from "react";
// number, onIncrease, onDecrease를 props로 받아옴
function Counter({ number, onIncrease, onDecrease }) {
return (
<div>
<h1>{number}</h1>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
);
}
export default Counter;
import React from "react";
import Counter from "../components/Counter";
import { useSelector, useDispatch } from "react-redux";
import { increaseAsync, decreaseAsync } from "../modules/counter";
function CounterContainer() {
const number = useSelector((state) => state.counter);
const dispatch = useDispatch();
const onIncrease = () => {
dispatch(increaseAsync());
};
const onDecrease = () => {
dispatch(decreaseAsync());
};
return (
<Counter number={number} onDecrease={onDecrease} onIncrease={onIncrease} />
);
}
export default CounterContainer;
const myLogger = (store) => (next) => (action) => {
console.log(action); // 먼저 액션을 출력합니다.
const result = next(action); // 다음 미들웨어 (또는 리듀서) 에게 액션을 전달합니다.
// 업데이트 이후의 상태를 조회합니다.
console.log("\t", store.getState()); // '\t' 는 탭 문자 입니다.
return result; // 여기서 반환하는 값은 dispatch(action)의 결과물이 됩니다. 기본: undefined
};
export default myLogger;
// 액션 타입
const INCREASE = "INCREASE";
const DECREASE = "DECREASE";
// 액션 생성 함수
export const increase = () => ({ type: INCREASE });
export const decrease = () => ({ type: DECREASE });
// setTimeout을 사용하여 액션이 디스패치 되는 것을 1초씩 딜레이 시킴
// getState를 쓰지 않는 다면 굳이 파라미터로 받아올 필요 없음
// increaseAsync와 decreaseAsync라는 thunk 함수를 만들었습니다
export const increaseAsync = () => (dispatch) => {
setTimeout(() => dispatch(increase()), 1000);
};
export const decreaseAsync = () => (dispatch) => {
setTimeout(() => dispatch(decrease()), 1000);
};
// 초기값
const initialState = 0;
export default function counter(state = initialState, action) {
switch (action.type) {
case INCREASE:
return state + 1;
case DECREASE:
return state - 1;
default:
return state;
}
}
import React from "react";
import CounterContainer from "./containers/CounterContainer";
function App() {
return <CounterContainer />;
}
export default App;
// 프로젝트에 리덕스 적용
// 프로젝트에 리덕스를 적용할 때는 src 디렉토리에 index.js에서
// 루트 리듀서를 불러와서 이를 통해 새로운 스토어를 만들고 Provider를 사용하여 적용한다
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import rootReducer from "./modules";
import logger from "redux-logger";
import ReduxThunk from "redux-thunk";
// logger를 사용하는 경우 logger가 가장 마지막에 와야함
const store = createStore(rootReducer, applyMiddleware(ReduxThunk, logger));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);