As i wish

[React] Redux-saga 적용하기 본문

React JS

[React] Redux-saga 적용하기

어면태 2019. 12. 6. 16:05

React 프로젝트를 하다 보면 자연스럽게 Redux 에 대하여 접하게 되고 그 외의 비동기 통신, 액션 컨트롤 등 다양한 것을 하기 위해 (대부분 비동기 통신) Redux-thunk, Redux-saga 를 접하게 됩니다.

이번 포스팅에서는 그런  Redux-saga 를 React 에 적용하는 법을 써보겠습니다.

일단 먼저 store 를 만듭니다.

store.js

import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga';
import rootSaga from './sagas';

const sagaMiddleware = createSagaMiddleware()

import reducers from './reducers';

const create = () => {
    const store = createStore(
      reducers,
      applyMiddleware(sagaMiddleware));

    sagaMiddleware.run(rootSaga);

    return store;
}

export default create;

 

sagas.js

import { all, put, call, takeEvery, delay } from 'redux-saga/effects'

// worker Saga: 비동기 증가 태스크를 수행할겁니다.
function* showPopup() {
  yield delay(2000);
  yield put({ type: 'SET_POPUP', payload: {text:'Test'}})
  // yield put({ type: 'HIDE_POPUP' })
}

// watcher Saga: 각각의 INCREMENT_ASYNC 에 incrementAsync 태스크를 생성할겁니다.
function* watchIncrementAsync() {
  yield takeEvery('SHOW_POPUP', showPopup)
}

export default function* rootSaga() {
   yield all([
     watchIncrementAsync(),
   ]);
}

그 다음 root 에다가 store 를 만든 뒤 Provider 에 props로 넣어줍니다.

client.jsx

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';

import create from './store';
import { hot } from 'react-hot-loader/root';

import Popup from './components/Popups'; // Class 사용

const Store = create();
const Hot = hot(Popup);

ReactDOM.render(
  <Provider store={Store}>
    <Hot />
  </Provider>,
  document.getElementById('root')
); // Class 사용

 

사실 보시면 알겠지만 Redux-saga 는 미들웨어이기 때문에 실제 Reducer를 붙여주는것과 동일하지만 중간에 한코드만 넣어주면 됩니다.

store.js 에서 applyMiddleware, constsagaMiddleware=createSagaMiddleware() 이것들이 바로 그것이죠!

 

기존 미들웨어 없이는 다음과 같이 store.js 를 구성해주면 됩니다. (redux-saga 없이 적용)

import { createStore } from 'redux'
import reducers from './reducers';

const create = () => {
    const store = createStore(reducer);

    return store;
}

export default create;

 

reducer.js

  
import * as actions from './actions';

const initialState = {
  text: ''
}

const reducer = (state=initialState, action) => {
  const { type, payload } = action;

  switch (type) {
    case actions.SET_POPUP:
      return {
        ...state,
        text: payload.text
      }
    case actions.HIDE_POPUP:
      return {
        ...state,
        text: ''
      };
    default:
      return state;
  }
};

export default reducer;

 

 

* 참고로 SSR 를 위해  next.js 에서 redux, redux-saga 를 사용하기 위해서는 몇가지 더 추가해야할 사항이 있습니다.

import React from 'react';
import Head from 'next/head';
import PropTypes from 'prop-types';
import withRedux from 'next-redux-wrapper'; // With redux in next
import withReduxSaga from 'next-redux-saga'; // With redux-saga in next
import { applyMiddleware, compose, createStore } from 'redux';
import createSagaMiddleware from 'redux-saga';
import { Provider } from 'react-redux';

import reducers from '../reducers';
import rootSaga from '../sagas';
import AppLayout from '../components/AppLayout';

const NodeBird = ({ Component, store, pageProps }) =>
  // Using Provider link react with redux
  (
    <Provider store={store}>
      <Head>
        <title>NodeBird</title>
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/antd/3.20.5/antd.css" />
        <link rel="stylesheet" type="text/css" charSet="UTF-8" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.css" />
        <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick-theme.min.css" />
      </Head>
      <AppLayout>
        <Component {...pageProps} />
      </AppLayout>
    </Provider>
  );

NodeBird.propTypes = {
  Component: PropTypes.elementType.isRequired,
  store: PropTypes.object.isRequired,
  pageProps: PropTypes.object.isRequired,
};

NodeBird.getInitialProps = async (context) => {
  console.log(context);
  const { ctx, Component } = context;
  let pageProps = {};
  if (Component.getInitialProps) {
    pageProps = await Component.getInitialProps(ctx);
  }

  return { pageProps };
};

const configureStore = (initialState, options) => {
  const sagaMiddleware = createSagaMiddleware();
  const middlewares = [sagaMiddleware];
  
  const enhancer = compose(applyMiddleware(...middlewares));
  const store = createStore(reducers, initialState, enhancer);
  
  store.sagaTask = sagaMiddleware.run(rootSaga);
  return store;
};

export default withRedux(configureStore)(withReduxSaga(NodeBird));

next.js 사용했을 때 다음과 같이 configureStore 에서 해줘야 각각의 page 내의 getInitailProps에서 redux 및 redux-saga 를 사용할 수 있습니다.

 

참고: 깃허브

Comments