8일차(7.1~7.4)

미들웨어 쓰는 이유?

  • 디스패치된 액션을 스토어로 전달하기 전에 처리하고 싶은 작업이 있을 때 비동기적으로 실행시키기 위해 사용 ( ⇒ reduce안에 정의된 상태를 조작하는 함수를 실행시키기 전에 뭔가를 처리해야할 때, 조건에 따라 실행시켜야 할 때 사용?)
  • 리덕스는 동기적인 흐름을 통해 동작
  • 동기적인 흐름만으로는 처리하기 힘든 작업들이 있다
  • 시간을 딜레이시켜 동작하게 한다던지
  • 외부 데이터를 요청하여 그에 따른 응답을 화면에 보여주어야 한다던지
  • 액션을 취소
  • 또 다른 액션을 발생
  • 종류
  • redux-logger: 로그를 남겨준다
  • redux-thunk: 비동기 작업을 처리

02. 미들웨어 만들어보고 이해하기

리덕스 미들웨어

  • 함수를 연달아서 두번 리턴하는 함수
  • store: 리덕스 스토어 인스턴스로 dispatchgetStatesubscribe 등의 내장함수 포함
  • next
  • 액션을 다음 미들웨어에게 전달하는 함수 next(action)
  • 다음 미들웨어가 없다면 리듀서에게 액션 전달
  • next 를 호출하지 않게 된다면 액션이 무시처리되어 리듀서에게로 전달되지 않는다
  • action: 현재 처리하고 있는 액션 객체
const middleware = store => next => action => {
  // 하고 싶은 작업...
}

function middleware(store) {
  return function (next) {
    return function (action) {
      // 하고 싶은 작업...
    };
  };
};

  • 리덕스 스토어에는 여러 개의 미들웨어를 등록할 수 있다
  • 새로운 액션이 디스패치 되면 첫 번째로 등록한 미들웨어 호출
  • 미들웨어에서 next(action)을 호출하게 되면 다음 미들웨어로 액션이 넘어감
  • 미들웨어에서 store.dispatch 를 사용하면 다른 액션을 추가적으로 발생시킬 수도 있다

미들웨어 생성

// middlewares/myLogger.js
// 단순히 전달받은 액션을 출력하고 다음으로 넘기는 작업

const myLogger = store => next => action => {
  console.log(action);
  const result = next(action); 

  console.log('\t', store.getState()); // 액션이 리듀서까지 전달되고 난 후의 새로운 상태 확인

  return result; 
};

export default myLogger;

미들웨어 적용

// index.js

import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import rootReducer from './modules';
import myLogger from './middlewares/myLogger';

const store = createStore(rootReducer, applyMiddleware(myLogger));
  • 미들웨어 안에서는 무엇이든지 할 수 있다
  • 액션 값을 객체가 아닌 함수도 받아오게 만들어서 액션이 함수타입이면 이를 실행시키게끔 할 수도 있다

03. redux-logger 사용 및 미들웨어와 DevTools 함께 사용하기

리덕스 관련 값들을 콘솔에 로깅하는건 이렇게 직접 만드는 것 보단

redux-logger 라는 미들웨어를 사용하는게 더욱 좋다

yarn add redux-logger

여러 개의 미들웨어를 등록할 수 있다

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import rootReducer from './modules';

import myLogger from './middlewares/myLogger'; // 만든 미들웨어
import logger from 'redux-logger'; // 라이브러리

const store = createStore(rootReducer, applyMiddleware(myLogger, logger)); // 여러개의 미들웨어를 적용 할 수 있습니다.

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

serviceWorker.unregister();

Redux DevTools

Redux DevTools를 미들웨어와 함께 사용해야 하는 방법

// index.js

import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';

const store = createStore(
  rootReducer,
  composeWithDevTools(applyMiddleware(logger))
);

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

serviceWorker.unregister();

04. redux-thunk

  • 비동기 작업을 처리 할 때 가장 많이 사용하는 미들웨어
  • 액션 객체가 아닌, 함수를 디스패치 할 수 있다
  • 디스패치하려는 함수는 dispatch 와 getState 를 파라미터로 받아와주어야 한다
  • import해서 createStore에 적용해주면 액션을 비동기로 실행되는 함수로 생성할 수 있다

// dispatch를 비동기로 실행하고 싶을 때 사용
const getComments = () => async (dispatch, getState) => {
  const id = getState().post.activeId;
  dispatch({ type: 'GET_COMMENTS' });
  try {
    const comments = await api.getComments(id);
    dispatch({ type:  'GET_COMMENTS_SUCCESS', id, comments });
  } catch (e) {
    dispatch({ type:  'GET_COMMENTS_ERROR', error: e });
  }
}

사용법

yarn add redux-thunk

// index.js

import ReduxThunk from 'redux-thunk';

const store = createStore(
  rootReducer,
  // logger 를 사용하는 경우, logger가 가장 마지막에 와야한다
  composeWithDevTools(applyMiddleware(ReduxThunk, logger))
);
// modules/counter.js

// 액션 타입
const INCREASE = 'INCREASE';
const DECREASE = 'DECREASE';

// 액션 생성 함수
export const increase = () => ({ type: INCREASE });
export const decrease = () => ({ type: DECREASE });

// 함수인 액션 - getState를 쓰지 않는다면 굳이 파라미터로 받아올 필요 없습니다.
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;
  }
}
// containers/CounterContainer.js

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());
  };
  
  // 기존 코드
	const onIncrease = () => {
    dispatch(increase());
  };
  const onDecrease = () => {
    dispatch(decrease());
  };

  return (
    <Counter number={number} onIncrease={onIncrease} onDecrease={onDecrease} />
  );
}

export default CounterContainer;

출처 https://react.vlpt.us/

좋은 웹페이지 즐겨찾기