Redux-thunk

Seunghwa's Devlog·2021년 3월 3일
0

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 })); // 실패시
};
  • thunk함수에서 async/await 사용
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 })); // 실패시
};

이제 실제로 한번 구현해보자!

  • redux-thunk 설치 및 적용
$ yarn add redux-thunk
  • src/components/Count.js 에서 Counter 작성
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;
  • src/containers/CounterContainer.js에서 CounterContainer 작성
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;
  • src/middlewares/myLogger.js 에서 myLogger 작성
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;
  • src/modules/counter.js 에서 counter, increaseAsync, decreaseAsync 작성
// 액션 타입
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;
  }
}
  • src/App.js 작성
import React from "react";
import CounterContainer from "./containers/CounterContainer";

function App() {
  return <CounterContainer />;
}

export default App;
  • src/index.js 작성
// 프로젝트에 리덕스 적용
// 프로젝트에 리덕스를 적용할 때는 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")
);
  • 실행결과
    Async를 사용하여 화면이 디스패치 되는것을 1초씩 딜레이 되는것을 확인할 수 있다.
profile
에러와 부딪히고 새로운 것을 배우며 성장해가는 과정을 기록합니다!

0개의 댓글