Redux-Saga image

Redux-saga란?

redux-saga 는 리액트/리덕스 애플리케이션의 사이드 이펙트, 예를 들면 데이터 fetching이나 브라우저 캐시에 접근하는 순수하지 않은 비동기 동작들을, 더 쉽고 좋게 만드는 것을 목적으로하는 라이브러리입니다.

먼저 Saga를 실행하기 위해 redux-saga 미들웨어를 리덕스 스토어에 연결하는 방법을 알아보자.

  • createSagaMiddle

    Saga를 Redux Store에 연결하기 위해서는 미들웨어를 사용해야 합니다.
    그러기 위해 createSagaMiddle를 사용할겁니다.

Ex) createSagaMiddle를 Redux 미들웨어를 통해 연결하기

import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'

import reducer from './reducers'
import mySaga from './sagas'

// saga 미들웨어를 생성합니다.
const sagaMiddleware = createSagaMiddleware()
// 스토어에 mount 합니다.
const store = createStore(
  reducer,
  // redux의 미들웨어로 sagaMiddleware를 사용합니다.
  applyMiddleware(sagaMiddleware)
)

// 그리고 saga를 실행합니다.
sagaMiddleware.run(mySaga)

// 애플리케이션을 render합니다.

이것으로 우리는 Saga와 redux Store를 연결했습니다.

Saga 만들기

Saga는 기본적을 동기가 아닌 비동기적인 처리를 하기위해 사용합니다.
먼저 Action을 처리해줄 Reducer를 아래와 같이 만들어 줍니다.

const initialState = {
    age:20
};

const reducer = (state=initialState, action) => {
    const newState = {...state};

    switch(action.type){
        case 'AGE_UP': 
            newState.age += action.value;
            break;

        case 'AGE_DOWN': 
            newState.age -= action.value;
            break;
    }
    return newState;
};

export default reducer;

그리고 우리의 App을 Saga를 사용할수 있게 만들어 보겠습니다.

import React, { Component } from "react";
import "./App.css";
import { connect } from "react-redux";

class App extends Component {
  render() {
    return (
      <div className="App">
        <div className="Age-label">
          your age: <span>{this.props.age}</span>
        </div>
        <button onClick={this.props.onAgeUp}>Age UP</button>
        <button onClick={this.props.onAgeDown}>Age Down</button>
      </div>
    );
  }
}

const mapStateToProps = state => {
  return {
    age: state.age
  };
};

const mapDispachToProps = dispatch => {
  return {
    onAgeUp: () => dispatch({ type: "AGE_UP", value: 1 }),
    onAgeDown: () => dispatch({ type: "AGE_DOWN", value: 1 })
  };
};
export default connect(
  mapStateToProps,
  mapDispachToProps
)(App);

일단 Saga를 한번 만들어 보겠습니다.

import { delay } from "redux-saga";
import { takeEvery, put } from "redux/saga/effects";

function* ageUpAsync() {
  yield delay(4000);
  yield put({ type: "AGE_UP_ASYNC", value: 1 });
}

export function* watchAgeUp() {
  yield takeEvery("AGE_UP", ageUpAsync);
}

Sagas 는 오브젝트들을 redux-saga 미들웨어에 yield 하는 제너레이터 함수 로 구현되었습니다. yield된 오브젝트들은 미들웨어에 의해 해석되는 명령의 한 종류입니다

위의 코드에서 보면 우리는 delay와 put, 그리고takeEvery를 import했습니다.
우선 delay를 보면 delay는 몇초간 지연시킨후 다음 코드를 실행시키는 역할을 해줍니다. 단지 그뿐입니다.

incrementAsync 제너레이터 함수는 1초 딜레이후 put이라는 함수를 실행시킵니다.
put 은 우리가 이펙트 라고 부르는 예중 하나입니다. 이펙트는 미들웨어에 의해 수행되는 명령을 담고있는 간단한 자바스크립트 객체입니다. put은 redux store에 dispatch하는 역할을 합니다.

다음으로, 우리는 watchIncrementAsync 라는 또다른 Saga를 만들었습니다. dispatch된 INCREMENT_ASYNC 액션을 바라보고, INCREMENT_ASYNC액션이 dispatch될때마다 incrementAsync 를 실행하기 위해 redux-saga 패키지가 제공하는 takeEvery 헬퍼 함수를 사용했습니다.

만약 Saga가 여러개를 묶어서 사용하고 싶으면 all Effect를 사용해서 아래와 같이 만들어줄수 있다.

import { all } from 'redux-saga/effects';

// all 함수를 통해 Saga들을 하나로 묶어줄수 있다.
export default function* rootSaga() {
  yield all([
    helloSaga(),
    watchIncrementAsync()
  ])
}

그후 아래와 같이 sagaMiddleware.run()에 넣어주면 된다.

sagaMiddleware.run(rootSaga)