ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Redux] 아주 아주 간단한 redux-saga 기본
    개발/React 2022. 7. 11. 20:03

    redux-saga는 generator 기능을 이용해서 만들어졌습니다.

    간단하게만 알아볼게요.

    제너레이터 생성 함수를 만들 때 function*을 이용합니다.

    yield를 만나면 흐름이 멈추고, yield를 통해 반환되는 값은

    제너레이터 함수에서 next()로 얻게 됩니다.

    mdn 참고

     

    코드로 동작 방식을 확인해봅시다.

    function* gen() {
      yield 1;
      yield 2;
      yield 3;
    }
    const generator = gen();

    초기 상태는 suspended 입니다.

    첫 next 함수은 제너레이터의 시작입니다.

    generator.next();

    yield로 반환되는 값이 value입니다. yield 1에 실행제어가 걸려 있는 상태고

    next()를 호출할 때마다 다음 yield에 걸립니다.

    마지막 yield까지 끝나면 done: true가 출력됩니다.

     

    무한덧셈을 할 수 있는 제너레이터도 만들 수 있습니다.

    function* infiniteAddGenerator() {
        let result = 0;
        while(true) {
            result += yield result;
        }
    }

    next() 함수의 첫 호출은 제너레이터의 시작이므로 인수를 넣어도 반영되지 않습니다.

    시작 후부터 인수를 넣으면 다음과 같이 나옵니다.

    generator의 이런 기능을 기억합시다.

     

    redux-thunk는 함수를 디스패치는 기능으로 비동기 처리를 할 수 있었는데요.

    redux-saga는 제너레이터를 이용해 액션 객체를 디스패치하면서도 비동기 처리를 할 수 있습니다.

    기존에 리듀서는 등록되어 있는 상태에서 redux-saga 코드를 추가합니다.

     

    import {
      delay,
      put,
      takeEvery,
      takeLatest,
      takeLeading,
    } from 'redux-saga/effects';
    
    const INCREASE_ASYNC = 'modules/INCREASE_ASYNC';
    const DECREASE_ASYNC = 'modules/DECREASE_ASYNC';
    
    export const increase = () => ({ type: INCREASE });
    export const decrease = () => ({ type: DECREASE });
    export const increaseAsync = () => ({ type: INCREASE_ASYNC });
    export const decreaseAsync = () => ({ type: DECREASE_ASYNC });
    
    function* increaseSaga() {
      yield delay(1000);
      yield put(increase());
    }
    
    function* decreaseSaga() {
      yield delay(1000);
      yield put(decrease());
    }
    
    // 내보내서 root saga 만들어야 함
    export function* counterSaga() {
      // 매 호출마다 처리
      yield takeEvery(INCREASE_ASYNC, increaseSaga);
      // 마지막에 호출된 거 하나만 처리
      yield takeLatest(DECREASE_ASYNC, decreaseSaga);
    }

    redux-saga/effects라는 곳에서 함수들을 가져오고 있습니다.

    effects는 redux-saga 미들웨어에게 작업을 요청하는 함수들을 가지고 있습니다.

    yield put이 disapatch와 유사합니다.

     

    INCREASE_ASYNC나 DECREASE_ASYNC가 디스패치되면

    각각 increaseSaga, decreaseSaga가 호출되면서 액션이 발생되므로

    리듀서에서 각 타입에 맞게 처리가 진행됩니다.

     

    import React from 'react';
    import { useSelector, useDispatch } from 'react-redux';
    import Counter from '../components/Counter';
    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;

     

    saga 함수는 리듀서들로 root 리듀서를 만든 것과 비슷하게 작업한 후 export 합니다.

    import { combineReducers } from 'redux';
    import counter, { counterSaga } from './counter';
    import posts from './posts';
    import { all } from 'redux-saga/effects';
    
    const rootReducer = combineReducers({ counter, posts });
    
    export function* rootSaga() {
      yield all([counterSaga()]);
    }
    
    export default rootReducer;

     

    index.js에서 미들웨어로 등록하면 됩니다.

    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import './index.css';
    import App from './App';
    import reportWebVitals from './reportWebVitals';
    import { Provider } from 'react-redux';
    import { legacy_createStore as createStore, applyMiddleware } from 'redux';
    import rootReducer, { rootSaga } from './modules';
    import logger from 'redux-logger';
    import { composeWithDevTools } from 'redux-devtools-extension';
    import ReduxThunk from 'redux-thunk';
    import { BrowserRouter as Router } from 'react-router-dom';
    import createSagaMiddleware from '@redux-saga/core';
    
    const sagaMiddleware = createSagaMiddleware();
    
    const store = createStore(
      rootReducer,
      composeWithDevTools(applyMiddleware(ReduxThunk, sagaMiddleware, logger))
    );
    
    sagaMiddleware.run(rootSaga);
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
      <React.StrictMode>
        <Router>
          <Provider store={store}>
            <App />
          </Provider>
        </Router>
      </React.StrictMode>
    );
    
    reportWebVitals();

     

    공식 예제는 공식 사이트를 참고해보세요.

    댓글

Designed by Tistory.