saga-begining

redux-saga 是一个用于管理 Redux 应用异步操作的中间件(又称异步 action)。 redux-saga 通过创建 Sagas 将所有的异步操作逻辑收集在一个地方集中处理,可以用来代替 redux-thunk 中间件。

这意味着应用的逻辑会存在两个地方:

  • Reducers 负责处理 action 的 state 更新。
  • Sagas 负责协调那些复杂或异步的操作。
  • 此篇文章重在完成saga的基本应用,其中会用到一些其他很有用的插件,saga详细说明请参考文档Redux-saga中文文档
  • 文章项目源码戳github

这里我们用saga完整一个Counter的使用

index.js—挂在到项目元素root上

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import createStore from './store/configureStore'
import App from './App';

const store = createStore();
ReactDOM.render(
  <Provider store={store}>
      <App />
  </Provider>,
  document.getElementById('root')
);

上面index.js用到的creatrStore.js

import {createLogger} from 'redux-logger';
// const createLogger = require('redux-logger');
import createSagaMiddleware from 'redux-saga';
import {createStore, applyMiddleware } from 'redux';
import rootReducer from '../redux'
import rootSaga from '../saga'

const middlewares = [];
// 创建中间件saga
const sagaMiddleware = createSagaMiddleware();
middlewares.push(sagaMiddleware)

if (process.env.NODE_ENV === 'development') {
    //创建中间件logger
    const logger = createLogger();
    middlewares.push(logger);
}

export default function configureStore(initialState) {
   const store = createStore(rootReducer, initialState, applyMiddleware(...middlewares));
   sagaMiddleware.run(rootSaga) //saga中间键挂在到store后需要run一下 所有的saga文件
   return store;
}

createStore用到的rootRedux

import {combineReducers} from 'redux';
const rootReducer = combineReducers({
  counterReducer1: require('./counterReducer1').reducer,
  counterReducer2: require('./counterReducer2').reducer,
})
export default rootReducer

createStore用到的rootSaga

import { takeLatest } from 'redux-saga/effects'
/* ------------- Types ------------- */
import { CounterTypes1 } from '../redux/counterReducer1'
import { CounterTypes2 } from '../redux/counterReducer2'
/* ------------- Sagas ------------- */
import { counterPlus1, counterMinus1 } from './counterSaga1'
import { counterPlus2, counterMinus2 } from './counterSaga2'
/* ------------- Connect Types To Sagas ------------- */
export default function * rootSaga () {
  yield [
    takeLatest(CounterTypes1.PLUS_NUM_SAGA, counterPlus1),
    takeLatest(CounterTypes1.MINUS_NUM_SAGA, counterMinus1),
    takeLatest(CounterTypes2.PLUS_NUM_SAGA2, counterPlus2),
    takeLatest(CounterTypes2.MINUS_NUM_SAGA2, counterMinus2),
  ]
}

rootRedux 引入的counterReducer1

import { createReducer, createActions } from 'reduxsauce'
import Immutable from 'seamless-immutable'
/* ------------- Types and Action Creators ------------- */
const { Types, Creators } = createActions({
  plusNum: ['text'], //type名驼峰,生成的是全大写PLUS_NUM
  minusNum: null,
  plusNumSaga: ['text'],
  minusNumSaga: ['text'],
})

export const CounterTypes1 = Types //这里导出是给saga监听使用
export default Creators
//导出action生成器 Creators,
//Creators.plusNum =(text) => {type:'PLUS_NUM' ,text:text}
/* ------------- Initial State ------------- */
export const INITIAL_STATE = Immutable({
  value:0
})
/* ------------- Reducers ------------- */
export const plus = (state,action) => {
  const { text } = action
  return state.merge({ value:state.value+text })
}
export const minus = (state,action) => {
  return state.merge({ value:state.value-1 })
}
/* ------------- Hookup Reducers To Types ------------- */
export const reducer = createReducer(INITIAL_STATE, {
  [Types.PLUS_NUM]: plus,
  [Types.MINUS_NUM]: minus
  //这里reducer监听Types是PLUS_NUM的action,执行对应的plus放法
})
// reducer生成器,生成对应type的reducer

rootSaga引入的counterSaga1

import { put,select,call } from 'redux-saga/effects'
import CounterActions1 from '../redux/counterReducer1'

// attempts to get state
// const getState = (state) => state.login

// let API={
//     request:(text)=>{
//         return fetch(...)
//     }
// }

export function * counterPlus1 (action) {
    // const data = yield select(selectState) //通过select可以获取全局state

  // const check = yield call(API.request, text) //如果有异步请求,用call调用
  // if(check.ok == 'ok'){
  //     yield put(CounterActions1.xxx(xxx, xxx))
  // }
   const {text} = action
   alert(text)
   yield put(CounterActions1.plusNum(2))

}

export function * counterMinus1 (action) {
   const {text} = action
   alert(text)
   yield put(CounterActions1.minusNum())
}

谨记

  • sagareducer都会监听action监听的顺序是先reducersaga,所以如果reducersaga都监听了相同的action,那么saga逻辑执行在reducer改变后的状态基础上,尽量不要用sagareducer监听相同的reducer
  • 相同的action内容,不管是谁触发的,所有监听此actionreduceraction都会触发,所以请区分好action的触发及监听操作
  • thunk一样(dispatch,getState) 第二个参数会将全局的state函数当参数传递进来,sagaselect(()=>fn(state))也可以调用全局state