学习理解Redux Middleware

Redux中的middleware其实就像是给你提供一个在action发出到实际reducer执行之前处理一些事情的机会。可以允许我们添加自己的逻辑在这段当中。它提供的是位于 action 被发起之后,到达 reducer 之前的扩展点。

加入middleware后,整个数据的流动如下图所示:

举个简单的例子,我们使用middleware将每次action的执行详细信息都打出来。就用官方demo中的todoApp来举例,我们先实现一个简单的reducer用来添加一个todo:

const todoApp = (state = {todo: []}, action) => {
if (action.type === ‘addTodo’) {
state.todo = […state.todo, action.value]
}
return state
}
然后再补上其他逻辑测试,用最原始的方法实现将每次action的执行信息log出来:

const redux = require(‘redux’)
const todoApp = (state = {todo: []}, action) => {
if (action.type === ‘addTodo’) {
state.todo = […state.todo, action.value]
}
return state
}
let store = redux.createStore(todoApp);
const action = {
type: ‘addTodo’,
value: ‘todo’,
}
console.log(‘state: ‘, store.getState());
console.log(‘action: ‘, action);
store.dispatch(action)
console.log(‘next state: ‘, store.getState())
如果不出什么意外的话,我们这段代码应该会成功运行,并且将这段log出这个action的运行情况, 如下图:

接下来我们将log这件事尝试使用redux的middleware来完成。

首先,根据我们之前的了解,middleware其实是一段在action到reducer之间的处理逻辑。我们都知道,标准的一个redux发送一个action是调用store自身的dispatch方法。那么,我们想要在一个action到达reducer之前去做些处理的话,最好的地方应该就是尝试将store的dispatch替换为我们自己的,在其中加上我们的处理逻辑,例如打印log这件事。

let store = redux.createStore(todoApp);
const next = store.dispatch;
const dispatchWithLog = (action) => {
console.log(‘state: ‘, store.getState());
console.log(‘action: ‘, action);
next(action);
console.log(‘next state: ‘, store.getState());
}
store.dispatch = dispatchWithLog;
我们将默认store的dispatch替换为自己的dispatchWithLog, 通过这种方式,完成了我们的需求,只要任何地方调用了store的diapatch去发送新的action, 我们都能讲其log出来,这个看起来已经有一点middleware的意思了。

虽然上面已经可以解决问题了,并且已经有点middleware的意思了,但是还有一点硬伤就是,需求多了就比较难搞了,例如就像官方上既需要log又需要Crash Reporting, 再通过这种方式去处理就显得很不优雅。Crash Reporting的middle的一个简单实现如下:

const next1 = store.dispatch;
const dispatchWithCreshReporting = (action) => {
try {
next1(action);
} catch (err) {
console.error(err);
}
}
store.dispatch = dispatchWithCreshReporting;
当然,redux本身给我们提供了包装过后的工具方法来专门应用middleware。其中也不是简单粗暴的替换store上的dispatch了。这个方法即为applyMiddleware。

官方的doc也给出了一个关于applyMiddleware的一个简单粗暴的直接替换dispatch的一个示例,如下:

function applyMiddlewareByMonkeypatching(store, middlewares) {
middlewares = middlewares.slice()
middlewares.reverse()

// Transform dispatch function with each middleware.
middlewares.forEach(middleware =>
store.dispatch = middleware(store)
)
}
关于先逆序middlewares再进行替换,这里主要是为了,让middleware的执行顺序按照我么传给他的array顺序来进行。就像我们上面直接替换的那个例子,越往后面进行替换dispatch的在执行过程中先运行。

当然,官放的具体实现中不是这么简单粗暴的直接替换的方式,因为一来不够优雅,这种方式在链式的调用过程中有可能出现问题。比如某一个middleware并不是同步执行的,这样在进行store.dispatch = middleware(store)就有可能到下一个middleware时,store.dispatch还没有被替换。因此,官方的middleware是接受一个next的参数来,来拿到dispatch,并不是直接从store上对dispatch进行操作的。

一般一个标准的middleware是这个样子的,我们使用最初的log的那个middleware来举例,让它接受一个next(就是一个下一个的dispatch方法),再返回一个dispatch方法。

function logger(store) {
return function(next) {
return function(action) {
console.log(‘state: ‘, store.getState());
console.log(‘action: ‘, action);
let result = next(action);
console.log(‘next state: ‘, store.getState());
return result;
}
}
}
function creshReporting(store) {
return function(next) {
return function(action) {
try {
return next(action);
} catch (err) {
console.error(err);
return next;
}
}
}
}
然后假设我们在apply时这样应用一下:

function applyMiddleware(store, middlewares = [logger, crashReporting]) {
middlewares = middlewares.slice()
middlewares.reverse()
let dispatch = store.dispatch
middlewares.forEach(middleware =>
dispatch = middleware(store)(dispatch)
)
return Object.assign({}, store, { dispatch })
}
这样就能够进行优雅的链式调用了。并且用上ES6箭头函数后,这样写出来会更加的优雅:

const logger = store => next => action => {
console.log(‘state: ‘, store.getState());
console.log(‘action: ‘, action);
let result = next(action);
console.log(‘next state: ‘, store.getState());
return result;
}
最后,其实redux middleware使用起来其实是非常的方便的,只需要记住applyMiddleware这个API即可。即const store = createStore(reducer, applyMiddleware(middlewares))
技术分析完毕,希望大家可以喜欢蜘蛛表格小编的分享,后续还会带来更多的分享

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值