redux-saga 是一个用于管理应用程序副作用(例如异步获取数据,访问浏览器缓存等)的库,它的目标是让副作用管理更容易,执行更高效,测试更简单,在处理故障时更容易。
你可能已经用了 redux-thunk 来处理数据的读取。不同于 redux thunk,你不会再遇到回调地狱了,你可以很容易地测试异步流程并保持你的 action
是干净的。
// action.js
import * as types from './types'
export default {
increment(count) {
return {
type: types.INCREMENT,
payload: count
}
}
}
Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的,reducer
就是一个纯函数,接收旧的 state
和 action
,返回新的 state
。现在只需要谨记 reducer 一定要保持纯净。只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。
// 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
}
}
// app.js
import React, { Component } from 'react';
import { connect } from 'react-redux'
import action from './store/action'
class App extends Component {
add = () => {
// 异步处理
setTimeout(() => {
let count = this.props.number + 1
this.props.increment(count)
}, 1000);
}
render() {
return (
<>
<div>
{this.props.number}
</div>
<button onClick={this.add}>
点击
</button>
</>
);
}
}
const mapStateToProps = (state) => {
return state
}
export default connect(mapStateToProps, action)(App);
异步都写在组件里面,处理完成组件都可以直接获取,就没有必要把数据写到store里面。如果其他地方也要使用异步修改状态,那么就没法复用,现在使用 redux-saga来解决这个问题。
// sagas.js
import * as types from './types'
import { put, delay, takeEvery } from 'redux-saga/effects'
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 watchIncrementAsync()
}
takeEvery
,用于监听所有的 ASYNC_INCREMENT
action,并在 action 被匹配时执行 incrementAsync
任务。incrementAsync
Saga 通过 delay(1000)
延迟了 1 秒钟,然后 dispatch 一个叫 INCREMENT
的 action。
// action.js
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
}
}
}
首先我们引入 ./sagas
模块中的 Saga。然后使用 redux-saga 模块的 createSagaMiddleware
工厂函数来创建一个 Saga middleware。我们必须使用 applyMiddleware
将 middleware 连接至 Store。然后使用 sagaMiddleware.run(helloSaga)
运行 Saga。
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 中的 incrementAsync 函数
let count = this.props.number + 1
this.props.asyncIncrement(count)
}
render() {
return (
<>
<div>
{ this.props.number }
</div>
<button onClick={this.asyncAdd}>
点击
</button>
</>
);
}
}
const mapStateToProps = (state) => {
return state
}
export default connect(mapStateToProps, action)(App);
通过把一些异步代码都封装到 saga 里面,这样更好的管理代码,提高了复用效率,比如可以在 saga 里面发送API请求获取数据等等。