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里面去实现异步,两者的效果一样,提高了代码的复用