TIL no.48 - Redux-thunk

김종진·2021년 3월 21일
1

Redux

목록 보기
2/2

Redux-thunk

Redux-thunk는 리덕스에서 비동기 작업을 처리 할 때 가장 많이 사용하는 미들웨어이다

미들웨어란?

기존의 리덕스는 액션이 발생하게 되면, dispatch를 통해 store에게 상태 변화의 필요성을 알리게 된다. 하지만 dispatch된 액션을 store로 전달하기 전에 처리하고 싶은 작업이 있을 수도 있다.

리덕스는 동기적으로 작동되는데 가령 데이터를 가져오는 로딩중의 화면을 잠시 보여주는 등의 비동기적인 작업을 처리하는 데 있어 리덕스 미들웨어가 주로 사용된다.

설치

npm install redux-thunk

적용

redux-thunk를 사용하기 위해선 미들웨어를 스토어에 적용시켜줘야한다.

store/configure.js

const configure = () =>
  createStore(rootReducer, composeEnhancers(applyMiddleware(...middlewares)));

export default configure;

index.js

// store 만들기
ReactDOM.render(
  // 라우트밑에 컴포넌트에서 스토어의 상태값들을 다 쓸수 있도록 설정
  <Provider store={store}>
    <Routes />
  </Provider>,
  document.getElementById("root")
);

서버로부터 데이터를 받아와 뿌려주기 위해 redux를 구성해보자!
Ducks 구조로 하나의 파일안에 필요한 내용들을 모두 작성할 수 있다.

modules/productList.js

import axios from 'axios';
import { createAction, handleActions } from 'redux-actions';
import { SERVER } from '../../config';

// APIS , 통신에 필요한 함수만 정의()

const getProductListDataAPI = () => axios.get(`${SERVER}/products`);

// Action 이름 정의 
// 이렇게 통신을 위한 타입을 정의시 상황 3개를 1세트로 쓰는게 일반적이다. 

const GET_PRODUCT_LIST_DATA = 'GET_PRODUCT_LIST_DATA';
const GET_PRODUCT_LIST_DATA_SUCEESS = 'GET_PRODUCT_LIST_DATA_SUCEESS';
const GET_PRODUCT_LIST_DATA_FAILURE = 'GET_PRODUCT_LIST_DATA_FAILURE';

// Redux-Thunk
// reducer에 보낼 액션 타입,내용을 정의

export const getProductListData = () => async (dispatch) => {
  dispatch({ type: GET_PRODUCT_LIST_DATA }); // 요청이 시작됨
  try {
    const res = await getProductListDataAPI(); // 데이터 가져오기
    dispatch({ type: GET_PRODUCT_LIST_DATA_SUCEESS, payload: res.data }); // 성공
  } catch (e) {
    dispatch({ type: GET_PRODUCT_LIST_DATA_FAILURE, payload: e }); // 실패
  }
};

// 초기값 설정
const initState = {
  productList: [],
  loading: false,
  error: null,
};

// Reducer
// 해당 액션을 받아온다.

export default handleActions(
  {
    [GET_PRODUCT_LIST_DATA_SUCEESS]: (state, { payload: data }) => ({
      ...state,
      productList: data,
      loading: false,
    }),
    [GET_PRODUCT_LIST_DATA]: (state, { payload: data }) => ({
      ...state,
      loading: true,
    }),
    [GET_PRODUCT_LIST_DATA_FAILURE]: (state, { payload: error }) => ({
      ...state,
      loading: false,
      error: error,
    }),
  },
  initState,
);

미들웨어를 사용함으로서 redux로 데이터 통신의 과정중 로딩화면을 제어할 수 있다.

modules/loading.js

import { createAction, handleActions } from 'redux-actions';
// import { initLoadingState } from "store/state";

const START_LOADING = 'loading/START_LOADING';
const FINISH_LOADING = 'loading/FINISH_LOADING';

export const startLoading = createAction(
  START_LOADING,
  (requestType) => requestType,
);

export const finishLoading = createAction(
  FINISH_LOADING,
  (requestType) => requestType,
);

const initialState = {};

const loading = handleActions(
  {
    [START_LOADING]: (state, action) => ({
      ...state,
      [action.payload]: true,
    }),
    [FINISH_LOADING]: (state, action) => ({
      ...state,
      [action.payload]: false,
    }),
  },
  initialState,
);

export default loading;

각각의 기능별로 만들어 둔 리듀서를 하나의 스토어로 합치기 위해 rootReducer를 만든다.

modules/index.js

import { combineReducers } from "redux";
import productList from "./productList";
import loading from './loading';

const rootReducer = combineReducers({
  productList,
  loading,
});


export default rootReducer;

이제 미들웨어를 활용한 리덕스 구조가 완성되었다.
원하는 컴포넌트에서 useSelector를 사용하여 상태값을 쉽게 가져올수 있다.

import { useDispatch, useSelector } from 'react-redux';
import { getProductListData } from '../../store/modules/productList';

const { productList, loading } = useSelector((state) => state.productList);

const dispatch = useDispatch();
useEffect(() => {
  dispatch(getProductListData());
 }, []);
profile
FE Developer

0개의 댓글