redux-saga 是一个用于管理应用程序副作用(例如异步获取数据,访问浏览器缓存等)的库,它的目标是让副作用管理更容易,执行更高效,测试更简单,在处理故障时更容易。

你可能已经用了 redux-thunk 来处理数据的读取。不同于 redux thunk,你不会再遇到回调地狱了,你可以很容易地测试异步流程并保持你的 action 是干净的。

Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的,reducer 就是一个纯函数,接收旧的 state 和 action,返回新的 state。现在只需要谨记 reducer 一定要保持纯净。只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。

所有通常处理异步都是在组件里面写

// app.js
import React, { Component } from 'react';
import { connect } from 'react-redux'
import action from './store/action'

class App extends Component {
  add = () => {
   // 异步的提交
    setTimeout(() => {
      this.props.increment(this.props.number + 1)
    }, 1000);
  }

  render() {
    return (
      <>
        <div>
          {this.props.number}
        </div>
        <button onClick={this.add}>点击</button>
      </>
    );
  }
}

const mapStateToProps = (state) => {
  return state
}

export default connect(mapStateToProps, action)(App);
// action.js
import * as types from './types'
export default {
  increment(count) {
    return { type: types.INCREMENT, payload: count}
  }
}
// reducer.js
import * as types from './types'

let initState = { number: 0 }

export default function (state = initState, action) {
  switch (action.type) {
    case types.INCREMENT:
      return { number: action.payload }
    default:
      return state
  }
}
// types.js
export const INCREMENT = 'INCREMENT'
export const ASYNC_INCREMENT = 'ASYNC_INCREMENT'

异步都写在组件里面,代码就很难达到复用,为了解决这样的问题,redux-saga派上用场了。

redux-saga

import * as types from './types'
export default {
  increment(count) {
    return { type: types.INCREMENT, payload: count }
  },
  // 添加一个 ASYNC_INCREMENT 类型的action
  asyncIncrement(count) {
    return { type: types.ASYNC_INCREMENT, payload: count }
  }
}

takeEvery,用于监听所有的 ASYNC_INCREMENT action,并在 action 被匹配时执行 incrementAsync 任务。incrementAsync Saga 通过 delay(1000) 延迟了 1 秒钟,然后 dispatch 一个叫 INCREMENT 的 action。

import * as types from './types'
import { put, delay, takeEvery, all } from 'redux-saga/effects'

export function* helloSaga() {
  console.log('start')
}
export function* incrementAsync(action) {
  yield delay(1000) // 使用这个函数去阻塞Generator
  yield put({ type: types.INCREMENT, payload: action.payload }) // 然后 dispatch 一个叫 INCREMENT 的 action
}

export function* watchIncrementAsync() {
  //用于监听所有的 ASYNC_INCREMENT action
  yield takeEvery(types.ASYNC_INCREMENT, incrementAsync)
}

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

中间件需要这样配置

import { createStore, applyMiddleware } from 'redux'
import reducer from './reducer'
import createSagaMiddleware from 'redux-saga'
import { rootSaga } from './sagas'
const sagaMiddleware = createSagaMiddleware()
let store = applyMiddleware(sagaMiddleware)(createStore)(reducer)
sagaMiddleware.run(rootSaga)
export default store

组件里面使用

import React, { Component } from 'react';
import { connect } from 'react-redux'
import action from './store/action'

class App extends Component {
  asyncAdd = () => {
  	// dispatch 一个叫 ASYNC_INCREMENT 的 action,这个被 saga 里面的 watchIncrementAsync 函数监听了
    this.props.asyncIncrement(this.props.number + 1)
  }

  render() {
    return (
      <>
        <div>
          {this.props.number}
        </div>
        <button onClick={this.asyncAdd}>点击</button>
      </>
    );
  }
}

const mapStateToProps = (state) => {
  return state
}

export default connect(mapStateToProps, action)(App);

现在把异步都封装到saga里面去实现异步,两者的效果一样,提高了代码的复用