react-saga

在 reducers 中的所有操作都是同步的并且是纯粹的,即 reducer 都是纯函数,纯函数是指一个函数的返回结果只依赖于它的参数,并且在执行过程中不会对外部产生副作用,即给它传什么,就吐出什么。但是在实际的应用开发中,可能希望做一些异步的(如Ajax请求,异步获取数据,访问浏览器缓存)且不纯粹的操作(如改变外部的状态),这些在函数式编程范式中被称为“副作用”。

Redux 的作者将这些副作用的处理通过提供中间件的方式让开发者自行选择进行实现。

redux-thunk 和 redux-saga 是 redux 应用中最常用的两种异步流处理方式。

redux-saga 可以对指定的 action 请求进行一次拦截,处理完所需的异步操作后,再发送其它 action 去修改 state

以一个计数器为例

当点击自增按钮时,发送一个 type 为 increaseAsync 的异步自增请求。

src/container/action.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 自增请求
export function handleIncreaseAction(num) {
return {
type: "increase",
num: num
};
}

// 自减请求
export function handleInReduceAction(num) {
return {
type: "inReduce",
num: num
};
}

// 异步自增请求
export function handleIncreaseActionAsync(num) {
return {
type: "increaseAsync",
num: num
};
}

Sagas 都用 Generator 函数实现

此处 saga 会调用 watchIncrementAsync 方法对 type 为 increaseAsync 的 action 进行监听,一但发现该请求发出,会调用 fetchCounter 方法。然后会阻塞 3s ,发出另外一个 action 去改变 state 的值。

src/container/saga.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { call, put, takeEvery, takeLatest } from 'redux-saga/effects'

// 创建 Effect
function* fetchCounter() {
yield call(asyncFn);
yield put({type: "increase"});
}

// 异步执行,测试用
function asyncFn() {
return new Promise((resolve)=> {
setTimeout(() => {
console.log('3s end');
resolve();
}, 3000);
})
}

function* watchIncrementAsync() {
yield takeLatest("increaseAsync", fetchCounter);
}

export default watchIncrementAsync;

src/configureStore.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// compose 用来组和插件和中间件
import { createStore, applyMiddleware, compose } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import createReducer from './reducers';
import createSagaMiddleware from 'redux-saga';
import counterSaga from './containers/Counter/saga.js'

// create the saga middleware
const sagaMiddleware = createSagaMiddleware()
let middleware = [sagaMiddleware]

// createStore 第一个参数为 reducer
var store;
if (window.__REDUX_DEVTOOLS_EXTENSION__) {
store = createStore(createReducer(), compose(applyMiddleware(...middleware), composeWithDevTools()))
} else {
store = createStore(createReducer(), applyMiddleware(...middleware))
}
sagaMiddleware.run(counterSaga)

export default function configureStore() {
return store;
}

中文文档 https://redux-saga-in-chinese.js.org

本文结束,感谢您的阅读