Redux - Redux saga

BigbrotherShin·2020년 3월 12일
1

Frontend

목록 보기
4/31
post-thumbnail

의의

Redux는 동기적으로 실행(바로 실행)되기 때문에 비동기적 명령을 내릴 수 없다. 이는 미들웨어인 Redux-saga를 쓰는 이유가 된다(비동기 동작 실행).
주로 서버에 비동기 요청을 할 때 컴포넌트에서 요청하지 않고 Saga에서 모아서 관리해주기 위하여 사용한다.

Saga는 이터레이터(iterator)의 next 메서드를 이펙트에 따라 알아서 해주는 제너레이터(generator)이다.

middleware의 구조

currying 기법을 사용한다.

const middleware = (store) => (next) => (action) => {
  console.log(action) // 다른 작업들을 여기에서 실행할 수 있도록
  
	next(action);
};

redux-saga 사용 예시

sagas/user.js

import { all, fork, takeLatest, call, put } from 'redux-saga/effects';
import { LOG_IN, LOG_IN_SUCCESS, LOG_IN_FAILURE } from '../reducers/user';

function loginAPI() {
  // 서버에 요청을 보내는 부분
}

function* login() {
  try {
    yield call(loginAPI);
    yield put({ //put은 dispatch와 동일
      type: LOG_IN_SUCCESS,
    });
  } catch (e) {
    console.error(e); // loginAPI 실패
    yield put({
      type: LOG_IN_FAILURE,
    });
  }
}

function* watchLogin() {
  yield takeLatest(LOG_IN, login);
}

export default function* userSaga() {
  yield all([fork(watchLogin)]);
}

call은 함수 동기적 호출
fork는 함수 비동기적 호출
put은 액션 dispatch

next.js와 redux

next-redux-wrapper

Next.js를 사용하고 있다면 Redux를 사용하기 위해 next-redux-wrapper 라이브러리를 사용해야한다.

yarn add 'next-redux-wrapper'

또는

npm -i 'next-redux-wrapper'

예시

_app.js

import React from 'react';
import Head from 'next/head';
import PropTypes from 'prop-types';
import withRedux from 'next-redux-wrapper'; // next-redux-wrapper 라이브러리 import
import AppLayout from '../components/AppLayout';
import { Provider } from 'react-redux';
import reducer from '../reducers';
import { createStore, compose, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import rootSaga from '../sagas';

const sagaMiddleware = createSagaMiddleware();

const makeStore = (initialState, options) => {
  // 여기에 store 커스터마이징
  const middlewares = [sagaMiddleware]; // 미들웨어는 action과 store 사이에서 동작
  // const enhancer = compose(applyMiddleware([...middlewares]));

  const composeEnhancers =
    typeof window === 'object' &&
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ &&
    process.env.NODE_ENV !== 'production'
      ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
          // Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
        })
      : compose;

  const enhancer = composeEnhancers(
    applyMiddleware(...middlewares),
    // other store enhancers if any
  );

  const store = createStore(reducer, initialState, enhancer);
  sagaMiddleware.run(rootSaga);
  return store;
};

function NodeBird({ Component, pageProps, store }) {
  return (
    <Provider store={store}>
      <Head>
        <title>Node Bird</title>
        <link
          rel='stylesheet'
          href='https://cdnjs.cloudflare.com/ajax/libs/antd/4.0.1/antd.css'
        />
      </Head>
      <AppLayout>
        <Component {...pageProps} /> {/* Component JSX 형식으로 작성할 것 */}
        {/* Next에서 pages 폴더 내의 Components를 자동적으로 props로 넣어줌 */}
      </AppLayout>
    </Provider>
  );
}

NodeBird.propTypes = {
  Component: PropTypes.elementType,
  props: PropTypes.object,
};

export default withRedux(makeStore)(NodeBird); // HoC로 redux-next-wrapper, Redux, Component를 연결

Redux Saga와 같이 사용

// Before this, import what you need and create a root saga as usual

const makeStore = (initialState, options) => {
    // 1: Create the middleware
    const sagaMiddleware = createSagaMiddleware();

    // Before we returned the created store without assigning it to a variable:
    // return createStore(reducer, initialState);

    // 2: Add an extra parameter for applying middleware:
    const store = createStore(reducer, initialState, applyMiddleware(sagaMiddleware));

    // 3: Run your sagas:
    sagaMiddleware.run(rootSaga);

    // 4: now return the store:
    return store
};
profile
JavaScript, Node.js 그리고 React를 배우는

0개의 댓글