react-redux 源码解析(2) -- Connect(上)

1,100 阅读3分钟

react-redux 源码解析(1) -- Provider
react-redux 源码解析(2) -- Connect(上)
react-redux 源码解析(3) -- Connect(下)

---------------------------------------分割线-----------------------------------------
续接上文,我们分析了Provider。既然Provider已经准备完毕,我们就要开始是用它。中间我们定然会用到dispathc推送state改变store的值,那么我们接下来就来看看如何向组件注入state和dispatch。

我们先来看看它是如何使用的,上代码:

class Home extends Component<props, any>{
  componentDidMount(){
    console.log(this.props)
  }
  render(){
    return (
      <div></div>
    )
  }
}

export default connect((state)=>({...state}), (dispatch)=>({dispatch}))(Home);

使用还是很简单,看的出来connect是个HOC,传入组件,注入state和dispathc。 这里就没什么多讲的了,我们还是直接来看connect源码,上代码:

export function createConnect({
  connectHOC = connectAdvanced,
  mapStateToPropsFactories = defaultMapStateToPropsFactories,
  mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
  mergePropsFactories = defaultMergePropsFactories,
  selectorFactory = defaultSelectorFactory,
} = {}) {
  return function connect(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps,
    {
      pure = true,
      areStatesEqual = strictEqual,
      areOwnPropsEqual = shallowEqual,
      areStatePropsEqual = shallowEqual,
      areMergedPropsEqual = shallowEqual,
      ...extraOptions
    } = {}
  ) {
    const initMapStateToProps = match(
      mapStateToProps,
      mapStateToPropsFactories,
      'mapStateToProps'
    )
    const initMapDispatchToProps = match(
      mapDispatchToProps,
      mapDispatchToPropsFactories,
      'mapDispatchToProps'
    )
    const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')

    return connectHOC(selectorFactory, {
      // used in error messages
      methodName: 'connect',

      // used to compute Connect's displayName from the wrapped component's displayName.
      getDisplayName: (name) => `Connect(${name})`,

      // if mapStateToProps is falsy, the Connect component doesn't subscribe to store state changes
      shouldHandleStateChanges: Boolean(mapStateToProps),

      // passed through to selectorFactory
      initMapStateToProps,
      initMapDispatchToProps,
      initMergeProps,
      pure,
      areStatesEqual,
      areOwnPropsEqual,
      areStatePropsEqual,
      areMergedPropsEqual,

      // any extra options args can override defaults of connect or connectAdvanced
      ...extraOptions,
    })
  }
}

export default /*#__PURE__*/ createConnect()

返回的是createConnect执行的结果,也就是 connect函数,不过有五个参数参数,在调用 createConnect就已经注入了,分别是: connectHOC、mapStateToPropsFactories、mapDispatchToPropsFactories、mergePropsFactories、selectorFactory。默认值分别是connectAdvanced、defaultMapStateToPropsFactories、defaultMapDispatchToPropsFactories、defaultMergePropsFactories、defaultSelectorFactory。我们继续往下看,等到了调用的时候,咱们在贴上源码,逐一分析。

const initMapStateToProps = match(
      mapStateToProps,
      mapStateToPropsFactories,
      'mapStateToProps'
    )
const initMapDispatchToProps = match(
  mapDispatchToProps,
  mapDispatchToPropsFactories,
  'mapDispatchToProps'
)
const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')

看得到一开始就定义了三个常量。我们先来看第一个initMapStateToProps,调用了match函数,传入了上文提到的defaultMapStateToPropsFactories函数。那么他们到底干了什么?不墨迹,上源码:

match 函数
function match(arg, factories, name) {
  for (let i = factories.length - 1; i >= 0; i--) {
    const result = factories[i](arg)
    if (result) return result
  }

  return (dispatch, options) => {
    throw new Error(
      `Invalid value of type ${typeof arg} for ${name} argument when connecting component ${
        options.wrappedComponentName
      }.`
    )
  }
}

-----------------------

defaultMapStateToPropsFactories 函数

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]

看的出来 defaultMapStateToPropsFactories返回的是个函数数组, match 是个循环调用函数数组的函数。返回wrapMapToPropsConstant或者 wrapMapToPropsFunc函数调用的返回。第一个函数中如果mapStateToProps是函数那么就返回 wrapMapToPropsFunc调用如果不是就返回undefined。第二个函数中,如果优质就返回wrapMapToPropsConstant调用,其他返回undefined。逻辑还是清晰的。 我们这边只考虑mapStateToProps为非空且为函数的情况。我们来看看wrapMapToPropsFunc函数,上代码

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)

      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
  }
}

wrapMapToPropsFunc的函数相对来说比较复杂,接受的参数是你传入的mapStateToProps函数(methodName的作用只是错误提示),返回的是初始化selector函数(initProxySelector)。当使用initProxySelector初始化selector的时候,返回的函数proxy实则为一个代理(proxy)。第一次执行proxy(selector)时,dependsOnOwnProps的值为true,所以相当于执行proxy.mapToProps(stateOrDispatch, ownProps)(detectFactoryAndVerify),然后将proxy.mapToProps属性设置为你所传入的mapStateToProps函数。这时候再去执行getDependsOnOwnProps的目的是去确定你传入的mapStateToProps是否需要传入props。然后再去执行proxy(stateOrDispatch, ownProps),这时候proxy.mapToProps已经不是之前的detectFactoryAndVerify而是你传入的mapStateToProps(所以不会出现死循环)。执行的结果就是mapStateToProps运行后的结果。如果prop是对象,将会直接传递给被包裹组件。但是我们之前讲过,mapStateToProps是可以返回一个函数的,如果返回的值为一个函数,这个函数将会被作为proxy的mapStateToProps,再次去执行proxy。
接下来的initMapDispatchToProps其实和此类似,同学们可以自行看看。
再来看一下mergePropsFactories是怎么定义的,我们主要还是看非空的情况,上代码:

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
    }
  }
}

wrapMergePropsFunc中涉及到性能优化,首先wrapMergePropsFunc返回一个初始mergeProps的函数(mergePropsProxy)。函数mergePropsProxy闭包一个变量hasRunOnce来记录mergeProps运行次数,在mergeProps第一次运行时,会保存第一次传入被包裹组件的的props,再以后的运行过程中,如果你传入的参数pure为true并且前后的mergedProps值不同时(比较函数你可以自定义)才会传入新的属性,否则将传入之前的缓存值,以此来优化不必要的渲染。
接下来就是传入参数,返回connectAdvanced方法调用。它就是我们connect的具体实现,我们在下一章中详细讲解,敬请期待。