阅读react-redux源码(三) - mapStateToPropsFactories、mapDispatchToPropsFactories和mergePropsFactories

mapStateToPropsFactories

import {
    
     wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps'

export function whenMapStateToPropsIsFunction(mapStateToProps) {
    
    
  return typeof mapStateToProps === 'function'
    ? wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps')
    : undefined
}

export function whenMapStateToPropsIsMissing(mapStateToProps) {
    
    
  return !mapStateToProps ? wrapMapToPropsConstant(() => ({
    
    })) : undefined
}

export default [whenMapStateToPropsIsFunction, whenMapStateToPropsIsMissing]

首先mapStateToPropsFactories是一个数组[whenMapStateToPropsIsFunction, whenMapStateToPropsIsMissing]

从名字中很轻易就能看出来数组中两个函数分别处理什么任务。从右往左分别处理了,mapStateToProps参数为假值和mapStateToProps传入的是函数的情况。

whenMapStateToPropsIsMissing:如果入参是假值,那么就返回默认的函数,否则返回undefined。这个undefined被match捕获之后,match会继续遍历factories的下一项,也就是whenMapStateToPropsIsFunction。

whenMapStateToPropsIsFunction:如果mapStateToProps入参是函数,那么就返回wrapMapToPropsFunc(mapStateToProps, ‘mapStateToProps’)的返回值,否则返回undefined。也就是说入参不是函数就会返回undefined。到这里mapStateToPropsFactories数组就结束了,如果最后返回的是undefined,那么match函数会返回一个抛出错误的函数,当执行这个函数的时候回抛出错误。

所以对于入参mapStateToProps来说,如果需要可以传入一个函数,用于筛选组件需要的store.state,否则可不传,但是不能传入非function类型的真值,例如:true,string、number、object之类的值,否则会报错。

wrapMapToPropsConstant 和 wrapMapToPropsFunc

import verifyPlainObject from '../utils/verifyPlainObject'

export function wrapMapToPropsConstant(getConstant) {
    
    
  return function initConstantSelector(dispatch, options) {
    
    
    const constant = getConstant(dispatch, options)

    function constantSelector() {
    
    
      return constant
    }
    constantSelector.dependsOnOwnProps = false
    return constantSelector
  }
}

// dependsOnOwnProps is used by createMapToPropsProxy to determine whether to pass props as args
// to the mapToProps function being wrapped. It is also used by makePurePropsSelector to determine
// whether mapToProps needs to be invoked when props have changed.
//
// A length of one signals that mapToProps does not depend on props from the parent component.
// A length of zero is assumed to mean mapToProps is getting args via arguments or ...args and
// therefore not reporting its length accurately..

// function mapStateToProps(state, ownProps) { }
export function getDependsOnOwnProps(mapToProps) {
    
    
  return mapToProps.dependsOnOwnProps !== null &&
    mapToProps.dependsOnOwnProps !== undefined
    ? Boolean(mapToProps.dependsOnOwnProps)
    : mapToProps.length !== 1
}

// Used by whenMapStateToPropsIsFunction and whenMapDispatchToPropsIsFunction,
// this function wraps mapToProps in a proxy function which does several things:
//
//  * Detects whether the mapToProps function being called depends on props, which
//    is used by selectorFactory to decide if it should reinvoke on props changes.
//
//  * On first call, handles mapToProps if returns another function, and treats that
//    new function as the true mapToProps for subsequent calls.
//
//  * On first call, verifies the first result is a plain object, in order to warn
//    the developer that their mapToProps function is not returning a valid result.
//
export function wrapMapToPropsFunc(mapToProps, methodName) {
    
    
  return function initProxySelector(dispatch, {
    
     displayName }) {
    
    
    const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
    
    
      return proxy.dependsOnOwnProps
        ? proxy.mapToProps(stateOrDispatch, ownProps)
        : proxy.mapToProps(stateOrDispatch)
    }

    // allow detectFactoryAndVerify to get ownProps
    proxy.dependsOnOwnProps = true

    proxy.mapToProps = function detectFactoryAndVerify(
      stateOrDispatch,
      ownProps
    ) {
    
    
      proxy.mapToProps = mapToProps
      proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)
      let props = proxy(stateOrDispatch, ownProps)

      // function mapToStateWrap() { return function mapToState(state, ownProps) {} }
      if (typeof props === 'function') {
    
    
        proxy.mapToProps = props
        proxy.dependsOnOwnProps = getDependsOnOwnProps(props)
        props = proxy(stateOrDispatch, ownProps)
      }

      if (process.env.NODE_ENV !== 'production')
        verifyPlainObject(props, displayName, methodName)

      return props
    }

    return proxy
  }
}

这两个包裹函数的目的则是为mapToProps函数增强一些功能,mapToPrps函数包括mapStateToProps和mapDispatchToProps。

wrapMapToPropsConstant:该函数会用一个返回常量的函数作为入参,并返回一个函数。被返回的函数的返回值也是一个函数,也就是说一共有三层,需要执行三次才能拿到结果,也就是入参函数返回的常量。

wrapMapToPropsFunc:这个函数比较复杂。它返回一个mapToProps的代理函数,代理函数增加了几个功能点:

  1. 判断mapStateToProps函数是否依赖组件props
  2. 首次调用处理mapStateToProps返回一个mapStateToProps函数的情况
  3. 首次调用验证mapStateToProps返回值是一个简单对象

首先定义一个proxy函数:

const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
    
    
  return proxy.dependsOnOwnProps
    ? proxy.mapToProps(stateOrDispatch, ownProps)
  : proxy.mapToProps(stateOrDispatch)
}

这个函数会检查是否依赖组件原来的props如果依赖则传入,否则不传入。

默认mapToProps函数依赖组件被传入的props:

扫描二维码关注公众号,回复: 13429186 查看本文章
proxy.dependsOnOwnProps = true

这一段比较烧脑了,定义了proxy函数中首次真正执行的proxy.mapToProps函数:

proxy.mapToProps = function detectFactoryAndVerify(
	stateOrDispatch,
 	ownProps
) {
    
    
  proxy.mapToProps = mapToProps 
  proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)
  let props = proxy(stateOrDispatch, ownProps)

  // function mapToStateWrap() { return function mapToState(state, ownProps) {} }
  if (typeof props === 'function') {
    
    
    proxy.mapToProps = props
    proxy.dependsOnOwnProps = getDependsOnOwnProps(props)
    props = proxy(stateOrDispatch, ownProps)
  }

  if (process.env.NODE_ENV !== 'production')
    verifyPlainObject(props, displayName, methodName)

  return props
}

首先这个函数是stateOrDispatch,而入参是stateOrDispatch和需要连接store的组件自身的props。

将mapToProps赋值给proxy.mapToProps,然后检查mapToProps是否依赖ownProps。然后执行 :

proxy.dependsOnOwnProps
    ? proxy.mapToProps(stateOrDispatch, ownProps)
  : proxy.mapToProps(stateOrDispatch)

上两行代码已经将proxy.dependsOnOwnProps和proxy.mapToProps换掉了所以不会形成死循环再次进入当前方法proxy.mapToProp方法。

获取到mapToProps的返回值,检查返回值,如果是function那么这个代理函数就会将这个props当做真正的mapToProps函数。然后再赋值并且执行一遍。

if (typeof props === 'function') {
    
    
	proxy.mapToProps = props
	proxy.dependsOnOwnProps = getDependsOnOwnProps(props)
	props = proxy(stateOrDispatch, ownProps)
}

再接下来则会判断新得到的props是否是简单对象。如果不是简单对象并且不在生产环境中就要报错,否则直接返回props。

小结:wrapMapToPropsFunc函数将将mapStateToProps和mapDispatchToProps归为一类,都是输入一个值,输出一个对象,过程都一样,所以具体代理的是mapDispatch还是mapState只和入参有关,代理的过程是一样的。

getDependsOnOwnProps

export function getDependsOnOwnProps(mapToProps) {
    
    
  return mapToProps.dependsOnOwnProps !== null &&
    mapToProps.dependsOnOwnProps !== undefined
    ? Boolean(mapToProps.dependsOnOwnProps)
    : mapToProps.length !== 1
}

这个函数相对简单,如果mapToProps有显示声明是否依赖ownProps如果有将其转换为boolean返回,否则使用隐式判断记mapToProps的入参有几个,如果不是1个(其实这里应该是大于1吧?)则认为入参至少是两个,而第二个参数就希望是ownProps。

verifyPlainObject

这个方法主要验证对象是否是简单对象,具体说只做存储数据用,具体为什么要求mapToProps函数返回的是简单对象,就并没有头绪了。 判断是否是简单对象的方法如下:

export default function isPlainObject(obj) {
    
    
  if (typeof obj !== 'object' || obj === null) return false

  let proto = Object.getPrototypeOf(obj)
  if (proto === null) return true

  let baseProto = proto
  while (Object.getPrototypeOf(baseProto) !== null) {
    
    
    baseProto = Object.getPrototypeOf(baseProto)
  }

  return proto === baseProto
}

如果入参不是对象则不是简单对象,因为连对象都不是。

获取对象原型,如果对象原型是null(Object.create(null))则是简单对象。

遍历原型链,找到原型上倒数第二个节点,与proto对于,如果相等则表示obj是简单对象,否则不是简单对象。

class A extends B {} A的实例就不是简单对象,因为A的原型是B.prototype而不是Object.prototype。

未解决的问题:为什么要遍历而不是检查两层原型链?如:

export default function isPlainObject(obj) {
    
    
  if (typeof obj !== 'object' || obj === null) return false

  let proto = Object.getPrototypeOf(obj)
  if (proto === null) return true
	
  return Object.getPrototypeOf(proto) === null
}

mapDispatchToPropsFactories

import {
    
     bindActionCreators } from 'redux'
import {
    
     wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps'

export function whenMapDispatchToPropsIsFunction(mapDispatchToProps) {
    
    
  return typeof mapDispatchToProps === 'function'
    ? wrapMapToPropsFunc(mapDispatchToProps, 'mapDispatchToProps')
    : undefined
}

export function whenMapDispatchToPropsIsMissing(mapDispatchToProps) {
    
    
  return !mapDispatchToProps
    ? wrapMapToPropsConstant(dispatch => ({
    
     dispatch }))
    : undefined
}

export function whenMapDispatchToPropsIsObject(mapDispatchToProps) {
    
    
  return mapDispatchToProps && typeof mapDispatchToProps === 'object'
    ? wrapMapToPropsConstant(dispatch =>
        bindActionCreators(mapDispatchToProps, dispatch)
      )
    : undefined
}

export default [
  whenMapDispatchToPropsIsFunction,
  whenMapDispatchToPropsIsMissing,
  whenMapDispatchToPropsIsObject
]

Factories从右往左mapDispatchToProps分别是object的情况,没有传的情况和是函数的情况。

其中使用的到的wrapMapToPropsConstantwrapMapToPropsFunc在上面有介绍,而bindActionCreators的入参希望是一个对象,对象对应的值是actionCreator,执行的结果则是将actionCreator用函数包起来,例如这样:

function bindActionCreator(actionCreator, dispatch) {
    
    
  return function() {
    
    
    return dispatch(actionCreator.apply(this, arguments))
  }
}

mergePropsFactories

import verifyPlainObject from '../utils/verifyPlainObject'

export function defaultMergeProps(stateProps, dispatchProps, ownProps) {
    
    
  return {
    
     ...ownProps, ...stateProps, ...dispatchProps }
}

export function wrapMergePropsFunc(mergeProps) {
    
    
  return function initMergePropsProxy(
    dispatch,
    {
    
     displayName, pure, areMergedPropsEqual }
  ) {
    
    
    let hasRunOnce = false
    let mergedProps

    return function mergePropsProxy(stateProps, dispatchProps, ownProps) {
    
    
      const nextMergedProps = mergeProps(stateProps, dispatchProps, ownProps)

      if (hasRunOnce) {
    
    
        if (!pure || !areMergedPropsEqual(nextMergedProps, mergedProps))
          mergedProps = nextMergedProps
      } else {
    
    
        hasRunOnce = true
        mergedProps = nextMergedProps

        if (process.env.NODE_ENV !== 'production')
          verifyPlainObject(mergedProps, displayName, 'mergeProps')
      }

      return mergedProps
    }
  }
}

export function whenMergePropsIsFunction(mergeProps) {
    
    
  return typeof mergeProps === 'function'
    ? wrapMergePropsFunc(mergeProps)
    : undefined
}

export function whenMergePropsIsOmitted(mergeProps) {
    
    
  return !mergeProps ? () => defaultMergeProps : undefined
}

export default [whenMergePropsIsFunction, whenMergePropsIsOmitted]

和上面两个factories一样的模式,主要看下函数wrapMergePropsFunc主要做的事情是缓存mergedProps,如果对比出

nextMergedProps计算出来的props和缓存的mergedProps一样,那么就直接返回缓存的mergedProps。其中最终的props主要有三个来源,一个是stateProps,一个是dispatchProps还有一个是ownProps。

猜你喜欢

转载自blog.csdn.net/letterTiger/article/details/107196945
今日推荐