redux compose 详解
前言
redux compose 用于 applyMiddleware
/ createStore enhancer
, 理解 compose 原理对于理解 redux 很有必要.
<!–more–>
Src
https://github.com/reactjs/redux/blob/v3.7.2/src/compose.js
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
Example - middleware
拿 redux 自己的 middleware 举例
const store = createStore(reducer, preloadedState, enhancer)
let dispatch = store.dispatch
let chain = []
const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
- 一个 middleware 原型为
store => next => action => { ... }
- chain = middleware.map 这里, 传了
store = middlewareAPI
, 成员原型为next => action => {...}
dispatch = compose(...chain)(store.dispatch)
compose(...chain)
// 假设 chain.length = 3
= function() {
return chain0(chain1(chain2(...arguments)))
}
执行 compose(...chain)(store.dispatch)
因 middleware 中 action => {…}
部分和 store.dispatch 原型一致, 称为一个 middleware 的 dispatch 部分
- chain[2] 即最后一个 item, next 参数 =
store.dispatch
, 返回一个action => {...}
, 称为 chain[2] 的 dispatch 部分 - chain[1] 倒数第二个 item, next 参数 = chain[2] dispatch 部分, 返回
action => {...}
, 成为 chain[1] 的 dispatch 部分 - chain[0] 第一个 item, next 参数 = chain[1] dispatch 部分, 返回
action => {...}
, 称为 chain[0] 的 dispatch 部分
最后 compose(...chain)(store.dispatch)
- 结果是被 compose 的 chain[0] 的 dispatch 部分
- 每一个 middleware 中的 next 是下一个 middleware 的 dispatch 部分
- 最后一个 middleware 的 next 是 redux store 的 dispatch
心得
如果有一堆有相同目的的方法需要执行, 可以使用 redux compose 将其串起来, 对外暴露一个唯一入口
例如 [fn1, fn2, fn…] 有相同的原型, 需要串起来, 可以这样做
fn = compose(...[
next => fn1,
next => fn2,
next => ...
])
fn(fn_default) // fn_default 作为 fn... 最后一个的 next 参数
实战
例如 vue-router hook 中, 如 beforeEach, 如果要很多不想关的事情要做, 可以这样做
const thing1 = composeNext => (to, from, next) => {
// something sync
somethingSync()
composeNext(to, from, next)
}
const thing2 = composeNext => (to, from, next) => {
// something async
someThingAsync(function(){
composeNext(to, from, next)
})
}
const thing3 = composeNext => (to, from, next) => {
// blabla
composeNext(to, from, next)
}
// vuex router hook 必须要调一次 next
const defaultThing = (to, from, next) => next()
const beforeEach = compose(...[
thing1,
thing2,
thing3,
])(defaultThing)
// vue-router
router.beforeEach(beforeEach)
// 这样thing1, thing2, thing3 通过 composeNext 串联起来
// 调用 comoseNext(to, from, next) 就是执行下一件 thing
// 这样的逻辑写在一个 function, 在包含异步的情况下, 分分钟流程出错...
有用的链接
博客地址 http://magicdawn.ml/2017/08/17/redux-compose/ 个人愚见, 不对请指正.