redux compose 详解
发布于 2 年前 作者 magicdawn 5867 次浏览 来自 分享

前言

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/ 个人愚见, 不对请指正.

回到顶部