You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/** * Composes single-argument functions from right to left. The rightmost * function can take multiple arguments as it provides the signature for * the resulting composite function. * * @param {...Function} funcs The functions to compose. * @returns {Function} A function obtained by composing the argument functions * from right to left. For example, compose(f, g, h) is identical to doing * (...args) => f(g(h(...args))). */exportdefaultfunctioncompose(...funcs){if(funcs.length===0){returnarg=>arg}if(funcs.length===1){returnfuncs[0]}returnfuncs.reduce((a,b)=>(...args)=>a(b(...args)))}
exportdefaultfunctionapplyMiddleware(...middlewares){returncreateStore=>(...args)=>{conststore=createStore(...args)letdispatch=()=>{thrownewError(`Dispatching while constructing your middleware is not allowed. `+`Other middleware would not be applied to this dispatch.`)}letchain=[]constmiddlewareAPI={getState: store.getState,dispatch: (...args)=>dispatch(...args)}chain=middlewares.map(middleware=>middleware(middlewareAPI))dispatch=compose(...chain)(store.dispatch)return{
...store,
dispatch
}}}
我们先执行第三个中间件,并把返回结果作为第二个中间件的入参继续执行,以此类推,下一个中间件的入参是上一个中间件的返回。如果说这里第三个中间件是上面的 redux-thunk,那么函数中的 next 就是 store.dispatch,返回第三个箭头函数 action。这里返回的第三个箭头函数,就是第二个中间件的 next 形参。以此类推,第二个返回的 action 就是第一个中间件的 next 形参。但是这里都还没真正开始执行中间件。
当我们外部调用 store.dispatch(action) 方法的时候,才要真正开始执行各个中间件。首先执行中间件 f1,当执行到 next 的时候,开始执行第二个中间件 f2,以此类推直到最后一个中间件,调用原生 store.dispatch 方法。
Uh oh!
There was an error while loading. Please reload this page.
redux 主要包含 5 个方法,分别是:
今天主要讲解下
applyMiddleware
和compose
这两个方法。在 redux 中引入了中间件的概念,没错如果你使用过 Express 或者 Koa 的话,一定不会对中间件陌生。我们知道,在 Koa 中,串联各个中间件的正是compose
方法,所以在 redux 中也同样使用了这个命名,作用也是串联所有中间件。reduce 用法
在正式讲解前,我们先来看下
reduce
的用法。根据 MDN 上的解释,参数
callback
执行数组中每个值的函数,包含四个参数:
initialValue
用作第一个调用 callback的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。
返回
函数累计处理的结果
compose 分析
有了上面 reduce 的基础,我们再来看下 compose 的代码。compose 的代码很简单,10行代码左右,但你看到
reduce
部分的时候,估计会一脸懵逼,短短的一行代码看上去却很绕。看注释,它的作用应该是
我们来推导下,它是怎么得出这个结果的。假设
funcs
等于[f1, f2, f3]
,其中f1
,f2
,f3
是三个中间件,(a, b) => (..args) => a(b(...args))
等于f
,那么funcs.reduce((a, b) => (...args) => a(b(...args)))
可以简化为[f1, f2, f3].reduce(f)
。第 1 次执行
f
:第 2 次执行
f
:通过上面的推导,证实了先前得出的结论
applyMiddleware 分析
通过上面的分析,我们知道
compose
是对中间件的串联,那么applyMiddleware
就是对中间件的应用了。最终返回createStore
中的方法以及经过中间件包装处理过的dispatch
方法。我们通过一个具体的中间件
redux-thunk
,来查看它内部到底是怎么来执行加载的中间件的。中间件中包含了三个箭头函数,在
applyMiddleware
中的map
操作后,返回了第二层箭头函数,所以chain
中存储的是各个中间件的第二层函数。根据
compose
的分析,我们先执行第三个中间件,并把返回结果作为第二个中间件的入参继续执行,以此类推,下一个中间件的入参是上一个中间件的返回。如果说这里第三个中间件是上面的
redux-thunk
,那么函数中的next
就是store.dispatch
,返回第三个箭头函数action
。这里返回的第三个箭头函数,就是第二个中间件的next
形参。以此类推,第二个返回的action
就是第一个中间件的next
形参。但是这里都还没真正开始执行中间件。当我们外部调用
store.dispatch(action)
方法的时候,才要真正开始执行各个中间件。首先执行中间件f1
,当执行到next
的时候,开始执行第二个中间件f2
,以此类推直到最后一个中间件,调用原生store.dispatch
方法。之所以要写这么绕,也是为了符合 redux 单一数据源的原则,applyMiddleware 的写法保证了 action 的流向,而且每一步的数据变化都是可以追踪的。
其他
对比了
4.0.0-beta.1
之前版本的applyMiddleware
的区别,发现内部dispatch
从之前的store.dispatch
改成了现在的直接抛出一个错误。根据这个 issues 的讨论,在中间件顶层调用了store.dispatch
,结果导致无法执行后面的中间件。这个调用应该是在处理map
操作的时候执行的,此时的applyMiddleware
还没执行完,store.dispatch
调用的还是原生createStroe
中的方法才导致的这个问题。另外如果在中间件中即
action
层使用dispatch
会怎样呢?我们知道我们可以通过next
进入到下个中间件,那如果调用store.dispatch
的话又会从外层重新来一遍,假如这个中间件内部只是粗暴的调用store.dispatch(action)
的话,就会形成死循环。如下图所示参考
The text was updated successfully, but these errors were encountered: