redux-saga
redux-saga
是一个用于管理应用程序 Side Effect
(副作用,例如异步获取数据,访问浏览器缓存等)的 library
,它的目标是让副作用管理更容易,执行更高效,测试更简单,在处理故障时更容易。
可以想像为,一个 saga
就像是应用程序中一个单独的线程,它独自负责处理副作用。 redux-saga
是一个 redux
中间件,意味着这个线程可以通过正常的 redux action
从主应用程序启动,暂停和取消,它能访问完整的 redux state
,也可以 dispatch redux action
。
redux-saga
使用了 ES6
的 Generator
功能,让异步的流程更易于读取,写入和测试。(如果你还不熟悉的话,这里有一些介绍性的链接) 通过这样的方式,这些异步的流程看起来就像是标准同步的 Javascript
代码。(有点像 async/await
,但 Generator
还有一些更棒而且我们也需要的功能)。
你可能已经用了 redux-thunk
来处理数据的读取。不同于 redux thunk
,你不会再遇到回调地狱了,你可以很容易地测试异步流程并保持你的 action
是干净的
API
redux-saga API
-
createSagaMiddleware(options)
- 作用: 创建一个
Redux middleware
,并将Sagas
连接到Redux Store
。
- 作用: 创建一个
-
middleware.run(saga, ...args)
- 作用: 动态地运行
saga
。只能 用于在applyMiddleware
阶段 之后 执行Saga
。
- 作用: 动态地运行
redux-saga/effects API
-
put(action)
-
作用: 创建一个
Effect
描述信息,用来命令middleware
向Store
发起一个action
。 这个effect
是非阻塞型的,并且所有向下游抛出的错误(例如在reducer
中),都不会冒泡回到saga
当中。 -
语法: 相当于
diapatch
yield put({type:'',payload:''});
-
-
call(fn, ...args)
-
作用: 创建一个
Effect
描述信息,用来命令middleware
以参数args
调用函数fn
。 -
语法: 异步调用函数
const result = yield call(异步函数,action);
-
-
fork(fn, ...args)
- 作用: 创建一个
Effect
描述信息,用来命令middleware
以 非阻塞调用 的形式执行fn
。
- 作用: 创建一个
-
take(pattern)
- 作用: 创建一个
Effect
描述信息,用来命令middleware
在Store
上等待指定的action
。 在发起与pattern
匹配的action
之前,Generator
将暂停。
- 作用: 创建一个
-
apply(context, fn, [args])
- 作用:
call([context, fn], ...args)
的另一种写法。
- 作用:
-
takeEvery(pattern, saga, ...args)
-
作用: 在发起(
dispatch
)到Store
并且匹配pattern
的每一个action
上派生一个saga
。 -
实现:
const takeEvery = (patternOrChannel, saga, ...args) => fork(function*() {
while (true) {
const action = yield take(patternOrChannel)
yield fork(saga, ...args.concat(action))
}
})
-
使用
架构设计方案A
store
目录结构:
|- index.js
|- reducers.js
|- watchers.js
- 开始安装相关依赖
yarn add redux react-redux redux-logger redux-saga -S
store > index.js
管理store
对象创建、配置
import Logger from 'redux-logger'
import watchers from './watchers';
import { countReducer } from "./reducers";
import createSagaMiddleware from 'redux-saga';
import { createStore, combineReducers, applyMiddleware } from "redux";
const reducer = combineReducers({
count: countReducer,
});
const saga = createSagaMiddleware();
const store = createStore(reducer, applyMiddleware(saga, Logger))
saga.run(watchers);
export default store;
store > reducers.js
管理reducer
export const countReducer = (state = 0, action) => {
const { type, payload } = action;
switch (type) {
case "add":
return state + payload;
case "min":
return state - payload;
default:
return state;
}
}
store > watchers.js
管理saga
import { call, put, takeEvery } from 'redux-saga/effects';
const asyncHandle = {
add(argu) {
console.log('add函数参数',argu);
return new Promise((resolve) => {
resolve(1);
});
},
min(argu) {
console.log('min函数参数',argu);
return new Promise((resolve) => {
resolve(1);
});
}
}
const effects = {
*handleAddCount(action) {
try{
const result = yield call(asyncHandle.add, action);
yield put({ type: 'add', payload: result });
} catch(error){
console.log(error);
}
},
*handleMinCount(action) {
try{
const result = yield call(asyncHandle.min, action);
yield put({ type: 'min', payload: result });
}catch(error){
console.log(error);
}
}
}
function* watcherCount() {
yield takeEvery('sagaAddCount', effects.handleAddCount);
yield takeEvery('sagaMinCount', effects.handleAddCount);
}
export default watcherCount;
根目录 > src > index.js
用于传递store
对象
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import store from './store'
import { Provider } from 'react-redux';
const render = () => {
ReactDOM.render(
<Provider store={store}><App /></Provider>,
document.getElementById('root')
);
}
render();
根目录 > src > App.js
用于调用saga
import React from 'react';
import { connect } from 'react-redux';
function App(props) {
return <div>
{props.count}
<button onClick={() => props.add(1)}>增加</button>
</div>
}
const mapStateToProps = (state) => {
return {
count: state.count
}
}
const mapDispatchToProps = {
add: (value) => ({ type: 'sagaAddCount', payload: value }),
min: (value) => ({ type: 'sagaMinCount', payload: value })
}
const ConnectApp = connect(mapStateToProps, mapDispatchToProps)(App);
export default ConnectApp;