e도서관/Redux-saga

나는 Redux-saga를 왜 쓰는가?

Heoky 2022. 1. 14. 16:06

Redux를 사용하다보니 dispatch가 동기적으로 이루어지는 것을 겪을 수 있었다.
내가 reducer에서 동작을 구현할 때, 한개의 dispatch가 성공적으로 작동할 경우 다음으로 넘어가고 싶은데 그럴때마다 설정한 state값을
불러와 상태 변화가 있을 경우 다음 dispatch를 또 실행해 주어야하는 번거로움이 있었다.

즉, dispatch를 여러번 할 때, 그만큼 dispatch를 여러번 써야했다. 그래서 비동기적으로 상태를 관리하기 위해 redux-saga를 도입했다.

redux-saga는 비동기적으로 dispatch를 할 수가 있는데 put을 통해 dispatch를 할 수 있다. 또한 내부 메서드를 통해 실수를 방지할 수 있는 기능이 있다. 예를 들자면, takeleast, thuttle 등.. 

redux-saga 사용전 알아야 할 선수 지식이 있다.

 


선수지식

generator function은 함수에 *를 붙이고 yield라는 것을 사용한다. 또한 next()를 통해 다음 yield를 호출 할 수 있다.

const gen = function* () {
  console.log(1);
  yield;
  console.log(2);
  yield;
  console.log(3);
  yield;
  console.log(4)
}
const gener = gen()
// gener() - gener{<suspended>}
gener().next() -> 1
gener().next() -> 2
gener().next() -> 3
gener().next() -> 4
gener().next() -> undifined

 


react에서 saga 사용하기

1. store에 saga 넣기

// configureStore.js
import { createWrapper } from 'next-redux-wrapper';
import { applyMiddleware, compose, createStore } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import createSagaMiddleware from 'redux-saga';

import reducer from '../reducers';
import rootSaga from '../sagas';

const sagaMiddleware = createSagaMiddleware();

const loggerMiddleware =
  ({ dispatch, getState }) =>
  (next) =>
  (action) => {
    console.log('loggerMiddleware(redux-thunk): ', action);
    return next(action);
  }; // 실헹되는 action을 console로 확인하기 위해 설정한 것

const configureStore = () => {
  const enhancer =
    process.env.NODE_ENV === 'production'
      ? compose(applyMiddleware(sagaMiddleware))
      : composeWithDevTools(applyMiddleware(sagaMiddleware, loggerMiddleware));
  const store = createStore(reducer, enhancer);
  store.sagaTask = sagaMiddleware.run(rootSaga);
  return store;
};

export const wrapper = createWrapper(configureStore, { debug: true });

2. rootSaga 만들기

// sagas/index.js
import { all, fork } from 'redux-saga/effects';

import userSaga from './user';
import postSaga from './post';

export default function* rootSaga() {
  yield all([fork(userSaga), fork(postSaga)]);
}

3. saga 모듈 만들기

// sagas/user.js
import { all, fork, put, call, takeLatest, delay } from 'redux-saga/effects';
import axios from 'axios';

import {
  LOGIN_REQUEST,
  LOGIN_SUCCESS,
  LOGIN_FAILURE,
} from '../reducers/user';

function loginAPI(data) {
  return axios.get('http://localhost:3000/user/user.json', data);
}

function* login(action) {
  try {
    // api 통신할때는 call
    const result = yield call(loginAPI, action.data);
    console.log(result);
    //yield delay(1000);
    // 아래와 같이 api 결과를 핸들링하여 dispatch 가능
    yield put({
      type: LOGIN_SUCCESS,
      data: action.data,
    });
  } catch (err) {
    yield put({
      type: LOGIN_FAILURE,
      data: err.response.data,
    });
  }
}

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

export default function* userSaga() {
  yield all([fork(watchLogin)]);
}
LOGIN_REQUEST 가 실행되면 정의한 reducer의 액션인  LOGIN_REQUEST 가 반응을  한다. 즉, saga의 액션이 실행되면
reducer도 함께 실행된다.