我们简单复习一下 createStore 的调用规则,示例代码如下:
// 引入 redux
import { createStore, applyMiddleware } from 'redux'
......
// 创建 store
const store = createStore(
reducer,
initial_state,
applyMiddleware(middleware1, middleware2, ...)
);
redux 对外暴露了 applyMiddleware 这个方法。applyMiddleware 接受任意个中间件作为入参,而它的返回值将会作为参数传入 createStore,这就是中间件的引入过程
中间件可以给我们增强createStore的能力,比如异步 我们可以看看我们常见的异步Action解决方案
在针对 Redux 源码主流程的分析中,我们不难看出这样一个规律——Redux 源码中只有同步操作,也就是说当我们 dispatch action 时,state 会被立即更新。
那如果想要在 Redux 中引入异步数据流,该怎么办呢?Redux 官方给出的建议是使用中间件来增强 createStore。支持异步数据流的 Redux 中间件有很多,其中最适合用来快速上手的应该就是 redux-thunk了。
// 引入 redux-thunk
import thunkMiddleware from 'redux-thunk'
import reducer from './reducers'
// 将中间件用 applyMiddleware 包装后传入
const store = createStore(reducer, applyMiddleware(thunkMiddleware))
redux-thunk 带来的改变非常好理解,它允许我们以函数的形式派发一个 action
// axios 是一个用于发起异步请求的库
import axios from 'axios'
// 引入 createStore 和 applyMiddleware
import { createStore, applyMiddleware } from 'redux';
// 引入 redux-thunk
import thunk from 'redux-thunk';
// 引入 reducer
import reducer from './reducers';
// 创建一个有 thunk 中间件加持的 store 对象
const store = createStore(
reducer,
applyMiddleware(thunk)
);
// 用于发起付款请求,并处理请求结果。由于涉及资金,我们希望感知请求的发送和响应的返回
// 入参是付款相关的信息(包括用户账密、金额等)
// 注意 payMoney 的返回值仍然是一个函数
const payMoney = (payInfo) => (dispatch) => {
// 付款前发出准备信号
dispatch({ type: 'payStart' })
fetch().then(res => { dispatch()})
return axios.post('/api/payMoney', {
payInfo
})
.then(function (response) {
console.log(response);
// 付款成功信号
dispatch({ type: 'paySuccess' })
})
.catch(function (error) {
console.log(error);
// 付款失败信号
dispatch({ type: 'payError' })
});
}
// 支付信息,入参
const payInfo = {
userName: xxx,
password: xxx,
count: xxx,
......
}
// dispatch 一个 action,注意这个 action 是一个函数
store.dispatch(payMoney(payInfo));
这里有两点需要注意的:
- 首先, dispatch 的入参从 action 对象变成了一个函数
- 其次,Redux 中间件将会在 action 被分发之后、到达 reducer 之前执行
如果有多个中间件
对于 Redux 中间件的工作模式,需要牢牢把握以下两点:
- 中间件的执行时机,即 action 被分发之后、reducer 触发之前;
- 中间件的执行前提,即 applyMiddleware 将会对 dispatch 函数进行改写,使得 dispatch 在触发 reducer 之前,会首先执行对 Redux 中间件的链式调用。
compose - 函数的合成 书写中间件 applyMiddleWare(thunk1, thunk2, thunk3, thunk4); 如何实现链式顺序调用? 这里就有一个compose方法,不考虑逻辑,核心的实现代码为
funcs.reduce((a, b) => (...args) => a(b(...args)))
这里巧妙的使用了array的reduce方法,关于reduce方法,可以参考 array.reduce
这里给出一个示例
const funcs = [f1,f2,f3,f4]
funcs.reduce((a, b) => (...args) => a(b(...args)))
| callback | a | b | currentIndex | return value |
|---|---|---|---|---|
| firstCall | f1 | f2 | 1 | (...args) =>f1(f2(...args)) |
| secondCall | (...args) =>f1(f2(...args)) | f3 | 2 | (...args) =>f1(f2(f3(...args)) |
| thirdCall | (...args) =>f1(f2(f3(...args)) | f4 | 3 | (...args) =>f1(f2(f3(f4(...args)) |
通过这种方式,可以将我们传入的多个thunk中间件,进行链式的执行。