likes
comments
collection
share

redux源码-详解compose函数

作者站长头像
站长
· 阅读数 46

前言

redux中如果我们想在dispatch或者其他store api中做一些其他事情,redux是支持我们加工store api并使用的。

加工store api???

首先定义一个函数sayHiOnDispatch

export const sayHiOnDispatch = (createStore) => {
  return (rootReducer, preloadedState, enhancers) => {
    const store = createStore(rootReducer, preloadedState, enhancers)
​
    function newDispatch(action) {
      const result = store.dispatch(action)
      console.log('Hi!')
      return result
    }
​
    return { ...store, dispatch: newDispatch }
  }
}

在定义store时传入此函数:

const store = createStore(rootReducer, undefined, sayHiOnDispatch)

观察效果:

在我们每次触发dispatch后控制台都会输出'Hi!'

这是因为sayHiOnDispatch接收createStore参数并重写dispatch方法返回newDispatch函数实现的。那如果我们不止有一个形如sayHiOnDispatch这样的加工函数怎么办呢?继续向后堆积参数吗?这样子是不行的:

在createStore函数中 只接收三个参数 如果传入的第四个参数也是一个函数 redux会抛出异常

function createStore(reducer, preloadedState, enhancer) {
  // ...
  if (
    (typeof preloadedState === 'function' && typeof enhancer === 'function') ||
    (typeof enhancer === 'function' && typeof arguments[3] === 'function')
  ) {
    throw new Error(
      'It looks like you are passing several store enhancers to ' +
        'createStore(). This is not supported. Instead, compose them ' +
        'together to a single function. See https://redux.js.org/tutorials/fundamentals/part-4-store#creating-a-store-with-enhancers for an example.'
    )
  }
  // ...
}

compose函数

为了解决以上场景,redux提供了compose函数。直接看下redux内部是如何实现的:

export default function compose(...funcs) {
  /**
   * 传入函数为空时compose()
   * compose帮助我们构建一个函数并返回
   */
  if (funcs.length === 0) {
    return (arg) => arg
  }
​
  /**
   * 只传入一个函数时compose(fn)
   * 直接返回fn
   */
  if (funcs.length === 1) {
    return funcs[0]
  }
​
  /**
   * 重点
   */
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

首先观察函数入参...funcs,将入参转为数组,compose(fn1, fn2, fn3) =>compose(...[fn1, fn2, fn3]),也就是说这里的funcs变量已经变成了一个数组,数组每项即是我们传入的各个函数。

接下来直接看重点,其实compose函数的核心也就是一行而已。如果不了解reduce方法,可以看这篇reduce方法详解

举例分析

假定有如下场景:

let num = 0
function add1(num) { return num + 1 }
function add2(num) { return num + 2 }
function add3(num) { return num + 3 }
​
add1(add2(add3(num))) // output: 6// 根据compose的功能 可以改写成如下 理论上也应该输出6
compose(add1, add2, add3)(num)

根据compose源码核心代码观察执行过程:

return funcs.reduce((a, b) => (...args) => a(b(...args))
值a值b每一轮循环返回值
第一轮循环add1add2(...args) => add1(add2(...args))
第二轮循环(...args) => add1(add2(...args))add3(...args) => add1(add2(add3(...args)))

有兴趣的小伙伴建议打个断点更能直观观察。

总结

逐步分析compose的执行过程并根据redux官网的解释:

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))).

从右向左组合单参数函数,最右边函数可以接受多个参数是因为其将为compose返回的最终函数提供入参。

参数:待组合的各个函数

返回值:通过从右到左组合参数函数获得的函数。例如,compose(f, g, h) 等同于(...args) => f(g(h(...args)))

其实第一眼看到这一行reduce代码时的确会有些发懵,建议大家遇到不懂的地方时通过debugger打断点的方式逐步分析,进而助于我们更快理解。

转载自:https://juejin.cn/post/7174834093933199421
评论
请登录