书接上文:
在上次案例我们使用 redux 做了一个加法器。现在我们想让它延迟两秒钟在加一。这就涉及异步处理了,只不过我们平时的异步处理是发送 Ajax 而已。
一、异步添加
- 第一种添加方法
<button onClick = {()=>{
setTimeout(()=>this.props.dispatch({type:"ADD"}),2000);
}}>按我加一</button>
- 第二种添加方法
import React,{Component} from "react";
import {connect} from "react-redux";
@connect(
({counterStore})=>({
a : counterStore.a
}),
(dispatch) => ({
add(){
setTimeout(()=>dispatch({type:"ADD"}),2000);
}
})
)
export default class App extends Component {
constructor(){
super()
}
render(){
return(
<div>
<h1>{this.props.a}</h1>
<button onClick = {this.props.add}>按我加一</button>
</div>
);
}
}
上面两种方法很明显都是通过事件来进行异步处理。所以从异步处理方面讲,完全不需要别的第三方库了,但是一旦项目足够大的话,异步处理显得混乱不堪。所以还是的借用地方法库。
二、redux-thunk
我们先把文件改一下结构:
import React,{Component} from "react";
import {connect} from "react-redux";
import *as action from "./action.js";
@connect(
({counterStore})=>({
a : counterStore.a
}),
(dispatch) => ({
dispatch
})
)
export default class App extends Component {
constructor(){
super()
}
render(){
return(
<div>
<h1>{this.props.a}</h1>
<button onClick = {()=>{this.props.dispatch(action.add())}}>按我加一</button>
</div>
);
}
}
action.js
export const add = ()=>({type:"ADD"});
现在开始安装依赖:
npm install --save redux-thunk
改变入口文件 main.js:
// ①引包
import thunk from "redux-thunk";
// ②加入中间件
const store = createStore(reducers,applyMiddleware(logger,thunk));
安装 thunk 之后,action 文件中就可以写高阶函数了,变成:
export const add = () => (dispatch)=>{setTimeout(()=>dispatch({type:"ADD"}),2000)};
其中,第二个圆括号中系统附送 dispatch。
三、redux-saga
saga 是英语 传奇 的意思。它的思想是 拦截。
安装依赖:
npm install --save redux-saga
npm install --save @babel/plugin-transform-runtime
@babel/plugin-transform-runtime
用来处理加星函数,我们还的去webpack.config.js 里面进行配置一下:
plugins: [
"@babel/plugin-transform-runtime"
]
然后改变入口文件 main.js 配置 saga
:
// ①引包
import createSagaMiddleware from "redux-saga";
// ②拦截文件
import mySaga from "./views/sagas/sagas.js";
// ③执行一下
const sagaMiddleware = createSagaMiddleware();
// ④放入中间件
const store = createStore(reducers,applyMiddleware(logger,sagaMiddleware));
// ⑤运行拦截文件
sagaMiddleware.run(mySaga);
新增的拦截文件 sagas.js:
import {takeEvery} from "redux-saga/effects";
export default function*(){
yield takeEvery("ADD",function*(){
alert("hello ! 我是 redux-saga!");
});
}
测试拦截 {type:"ADD"}
命令。
这时候就可以发送异步请求完成上面的业务延迟加一:
一般情况下我们使用 redux-saga 的命名规范需要注意下,使用 dispatch 发送的命令全部大写就行,类似
ADD
,到我们 拦截器
里面使用 put
发送命令,一般加个_
变成_ADD
以示区别。App.js 里面发送
this.props.dispatch({type:"ADD"})
sagas.js 拦截延迟并发送:
//注意引包路径 redux-saga/effects
// 延时器这里是能只用delay不能使用setTimeout
// takeEvery序列监听拦截
// put 类似APP里面的dispatch
// 函数有一个星,加星函数作为产生器
import {takeEvery,delay,put} from "redux-saga/effects";
export default function*(){
yield takeEvery("ADD",function*(){
yield delay(2000);
yield put({type:"_ADD"});
});
}
四、redux-saga 深入了解
1. 辅助函数
redux-saga 提供了一些辅助函数,包装了一些内部方法,用来在一些特定的 action 被发起到 Store 时派生任务。第一个函数 takeEvery ,是最常见的,它提供了类似 redux-thunk 的行为,takeEvery 允许多个 回调处理函数同时启动。在某个特定时刻,尽管之前还有一个或多个回调处理尚未结束,我们还是可以启动一个新的任务。如果我们只想得到最新那个请求的响应(例如,始终显示最新版本的数据)。我们可以使用 takeLatest 辅助函数,任何时刻 takeLatest 只允许一个任务在执行。并且这个任务是最后被启动的那个。 如果已经有一个任务在执行的时候启动另一个任务 ,那之前的这个任务会被自动取消。
2. Effect Creators
redux-saga 框架提供了很多创建 effect 的函数,下面我们就来简单的介绍下开发中最常用的几种:
- take(pattern) :监听且只监听一次 action
- put(action): 产生一个 action,也可以认为 put 就等于 dispatch 就可以。
- call(fn, ...args):阻塞地调用一个函数
- fork(fn, ...args):非阻塞地调用一个函数
- select() 直接拿到 redux 里面的值
- delay(延迟)
const v = yield select(({ counterReducer }) => counterReducer.v);
一个常用的请求案例模板配合 axios 使用:all 用来平行监听函数执行。
记住一个顺口溜:噗噗考
import { takeEvery , put , select } from "redux-saga/effects";
function* workSaga() {
const a = yield select(({counterStore})=>counterStore.a);
//真正发出请求
const { m } = yield fetch(`/api/${a}`).then(data => data.json());
const { m } = yield axios(`/api/${a}`).then(data => data.data);
yield call(send,m);
}
function* send(m) {
//转发出去
yield put({ "type": "ADD", m });
}
//watchSaga
function* workSaga(){
//takeEvery表示拦截
yield takeEvery('ADDSERVER', workSaga);
}
// rootSaga
export default function* rootSaga(){
//all表示平行运行
yield all([
workSaga()
]);
}