在Redux中,还有一个很重要的概念就是中间件,下面就看看Redux中的中间件是什么?帮我们解决了什么问题?
在前面几篇介绍中,所有的Action都是同步的行为,也就是说当dispatch一个Action之后,Store中的state会被立即更新。
但是,在现实的应用场景中,很多Action都是异步的,最常见的就是对于服务端的异步访问。
为了模拟一个异步行为,这里对前面章节的任务管理应用的例子进行简单的修改,模拟一个异步的添加任务的行为:
function asyncAddTask(name, category) {
return function(dispatch) {
setTimeout(function() {
dispatch({
type: ADD_TASK,
name,
category
});
}, 2000);
}
}
当运行修改后的代码时,会遇到下面的错误:
Error: Actions must be plain objects. Use custom middleware for async actions.
错误信息中给了比较详细的提示,Action必须是plain object,对于异步action需要使用中间件。
一般来说,中间件都有链式组合特性,可以对数据进行流式处理。
简单的说,在一些情况下,我们需要通过中间件帮我们进行特定的数据处理,从而得到我们想要的数据:
A -----> B
A ---> middleware 1 ---> middleware 2 ---> middleware 3 --> ... ---> B
这里就是对中间件的一个简单的认识,下面看看Redux中的中间件。
在Redux中,Middlerwares主要的作用就是处理Action。
Redux中的Action必须是一个plain object。但是为了实现异步的Action或其他功能,这个Action可能就是一个函数,或者是一个promise对象。这是就需要中间件帮助来处理这种特殊的Action了。
也就是说,Redux中的Middleware会对特定类型action做一定的转换,所以最后传给reducer的action一定是标准的plain object。
针对Action的特征,Reudx Middleware可以采取不同的操作:
- 可以选择传递给下一个中间件,如:next(action)
- 可以选择跳过某些中间件,如:dispatch(action)
- 或者更直接了当的结束传递,如:return。
Redux中常用的中间件:
- redux-thunk:action可以是一个函数,用来发起异步请求。
- redux-promise:action可以是一个promise对象,用来更优雅的进行异步操作。
- redux-logger:action就是一个标准的plain object,用来记录action和nextState的。
在Redux中,Middleware就是严格遵守下面模式的函数:
var someMiddleware = function ({ dispatch, getState }) {
return function(next) {
return function (action) {
// your middleware-specific code goes here
}
}
}
关于这个Middleware的模式函数,是一个三层嵌套的函数组成:
-
第一层函数暴露一个store API的子集给middleware(dispatch函数和getState函数),这两个参数可以给内层函数提供触发action和获取state的能力
-
第二层函数提供了中间件的链式组合能力,通过next,我们可以显式的把该中间件处理过的输入传给下一个中间件
-
第三层函数支持从上一个中间件或者dispatch传递过来的action参数,在函数内部经可以结合该action来确定下一步操作了
- 可以选择传递给下一个中间件,如:next(action)
- 可以选择跳过某些中间件,如:dispatch(action)
- 可以结束传递,如:return
下面回到如何处理异步行为,Redux中通常使用redux-thunk来处理异步的行为。
找到redux-thunk的源码如下(下面的是修改后版本),就简简单单的几行,严格遵守了Middleware pattern:
var thunkMiddleware = function({ dispatch, getState }) {
// console.log('Enter thunkMiddleware');
return function(next) {
// console.log('Function "next" provided:', next);
return function (action) {
// console.log('Handling action:', action);
// 如果action是一个函数,就是执行action(dispatch, getState)
return typeof action === 'function' ?
action(dispatch, getState) :
next(action)
}
}
}
有了thunkMiddleware的支持之后,就可以支持异步的asyncAddTask了,通过下面的命名运行异步action的代码:
npm run example "demo/05.01.asyncAction.js"
Action的处理变成了下面的数据流:
asyncAddTask(action creator) ---> action ---> dispatcher ---> thunkMiddleware ---> reducers
代码的执行结果如下,异步的action可以正常运行。
注意由于action是异步执行的,所以为了能够得到正常的结果,一定要等到action执行完毕之后再对store进行unsubscribe:
initial state is: { category: 'All', tasks: [] }
-> SELECT_CATEGORY Action
State change to: { category: 'Reading', tasks: [] }
-> ADD_TASK Action
State change to: { category: 'Reading',
tasks: [ { name: 'Write a blog', category: 'Writing' } ] }
-> ADD_TASK Action
State change to: { category: 'Reading',
tasks:
[ { name: 'Write a blog', category: 'Writing' },
{ name: 'Read ES6 spec', category: 'Reading' } ] }
run unsubscribe after async action complete