资讯专栏INFORMATION COLUMN

庖丁解牛React-Redux(二): connect

Taste / 3165人阅读

摘要:上篇文章庖丁解牛一介绍了的几个重要的原理,其中是函数的基础,这篇文章将主要介绍函数的原理。这意味着任何时刻更新,将会被调用。随后便是函数的重点部分,根据是否为选择恰当的,然后返回。最终执行函数,缓存结果并传入被包裹的组件。

connect API

  上篇文章庖丁解牛React-Redux(一): connectAdvanced介绍了react-redux的ProviderconnectAdvanced几个重要API的原理,其中connectAdvancedconnect函数的基础,这篇文章将主要介绍connect函数的原理。之前没有阅读过connectAdvanced最好提前阅读一下这篇文章。之前的文章有读者反映看起来比较晦涩,所以我准备随后会出一篇关于类似图解connectAdvanced的文章,不讲代码,主要从原理的方面诠释connectAdvanced。再次做个广告,欢迎大家关注我的掘金账号和我的博客。

  最开始我们还是来介绍一下connect函数:
  

</>复制代码

  1. connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

  将React组件连接到Redux store,connect函数是connectAdvanced的正面,为大多数常见场景提供了易于使用的API。connect函数并不会修改传递的组件,相反,它会返回一个新的,连接到store的组件类。

参数:

[mapStateToProps(state, [ownProps]): stateProps](Function):
如果这个参数被传递,返回新的组件将会订阅Redux的store的更新(update)。这意味着任何时刻store更新,mapStateToProps将会被调用。mapStateToProps必须返回一个纯对象(plain object),这个对象将会合并进组件的属性(props)。如果你不想订阅store的更新,可以给mapStateToProps参数传递null或者undefined

如果你的mapStateToProps函数被声明接受两个参数,mapStateToProps在调用时第一个参数是store state,传递给连接组件(connected component)的属性将会被作为第二个参数。如果连接组件接受到新的props(浅比较),mapStateToProps也会再次调用。

</>复制代码

  1. 注意: 在一些更高级的情况下,你需要更好的控制渲染的性能,mapStateToProps可以返回一个函数。这种场景下,返回的函数将会被作为特定组件实例的mapStateProps()函数。这允许你可以对每个实例缓存。但大部分应用用不到。

  2. mapStateToProps函数接受一个参数: Redux中store的state,并返回一个对象作为属性返回给被包裹的组件。这通常被称为`selector。

mapDispatchToProps(dispatch, [ownProps]): dispatchProps:

如果传入参数是一个对象,对象中的每个函数都被认为是Redux的action creator函数。返回的对象中的每个action creator函数都会被dispatch所包裹,因此可以直接调用,最终会被合并进入组件的属性。

如果传递一个函数,该函数的第一个参数为dispatch。需要你返回一个对象,其中的属性以你的方式将dispatch与action creator相绑定。

如果你的mapDispatchToProps函数声明接受两个参数,第一个函数是dispatch,第二个参数是传递给连接组件的属性。每当连接组件收到新的参数时,mapDispatchToProps就会被再次调用。

如果没有传入自定义的mapDispatchToProps函数或者对象,默认的mapDispatchToProps将为你的组件注入dispatch属性。

</>复制代码

  1. 注意: mapDispatchToProps也可以返回函数,用法与mapStateToProps相同

mergeProps(stateProps, dispatchProps, ownProps): props:

如果指定了这个参数,传入的参数为:函数 mapStateToProps()mapDispatchToProps()的运行结果以及传入连接组件的属性。从该函数返回的对象将会被当做属性传递给被包裹的组件。你可能会指定这个函数来基于props来选择性传入state,或者按照传入props绑定action creator。如果你省略了这个函数,默认是实现方式是:`
Object.assign({}, ownProps, stateProps, dispatchProps)
`

options
如果你指定了这个选项,更进一步自定义connector的行为。除了可以传入connectAdvanced的选项,还可以接受额外的选项:

[pure] (Boolean): 如果参数为true,用来避免重新渲染并调用mapStateToPropsmapDispatchToPropsmergeProps时基于各自的等值比较函数来比较所涉及到的stateprops对象。

[areStatesEqual] (Function): 如果参数puretrue,用来比较传入的store与之前的store值。默认值: strictEqual (===)。

[areOwnPropsEqual] (Function):如果参数puretrue,用来比较传入的props与之前的props值。默认值: strictEqual (===)。

[areStatePropsEqual] (Function):如果参数puretrue,用以比较mapStateToProps函数的结果与之前的结果值。

[areMergedPropsEqual] (Function): 如果参数puretrue,比较mergeProps函数的结果与之前的值。默认值为:shallowEqual。

[storeKey] (String): 用以从context获取store的key值。你仅仅可能在有多个store值的情况下才需要这个选项,默认值为:store

connect源码

  connect的代码如下:

</>复制代码

  1. export function createConnect({
  2. connectHOC = connectAdvanced,
  3. mapStateToPropsFactories = defaultMapStateToPropsFactories,
  4. mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
  5. mergePropsFactories = defaultMergePropsFactories,
  6. selectorFactory = defaultSelectorFactory
  7. } = {}) {
  8. return function connect(
  9. mapStateToProps,
  10. mapDispatchToProps,
  11. mergeProps,
  12. {
  13. pure = true,
  14. areStatesEqual = strictEqual,
  15. areOwnPropsEqual = shallowEqual,
  16. areStatePropsEqual = shallowEqual,
  17. areMergedPropsEqual = shallowEqual,
  18. ...extraOptions
  19. } = {}
  20. ) {
  21. const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, "mapStateToProps")
  22. const initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, "mapDispatchToProps")
  23. const initMergeProps = match(mergeProps, mergePropsFactories, "mergeProps")
  24. return connectHOC(selectorFactory, {
  25. methodName: "connect",
  26. getDisplayName: name => `Connect(${name})`,
  27. shouldHandleStateChanges: Boolean(mapStateToProps),
  28. initMapStateToProps,
  29. initMapDispatchToProps,
  30. initMergeProps,
  31. pure,
  32. areStatesEqual,
  33. areOwnPropsEqual,
  34. areStatePropsEqual,
  35. areMergedPropsEqual,
  36. ...extraOptions
  37. })
  38. }
  39. }
  40. const connect = createConnect();

  createConnect作为高阶函数,返回connect函数,通过柯里化的方式首先接受以下参数: connectHOCmapStateToPropsFactoriesmapDispatchToPropsFactoriesmergePropsFactoriesselectorFactory
 

connectHOC

  传入用来生成连接到store的高阶组件(HOC),默认是之前介绍过的connectAdvanced
  

selectorFactory

  selectorFactory用来生成selector,第一个参数将传入connectAdvanced。我们知道传入connectAdvancedselectorFactory函数主要是初始化selector函数。selector函数在每次connector component需要计算新的props都会被调用,selector函数会返回纯对象(plain object),这个对象会作为props传递给被包裹的组件(WrappedComponent)。selectorFactory的函数签名为:

</>复制代码

  1. selectorFactory(dispatch, factoryOptions): selector(state, ownProps): props (Function)

  我们来看看reduxselectorFactory是怎么定义的:
 

</>复制代码

  1. const selectorFactory = finalPropsSelectorFactory(dispatch, {
  2. initMapStateToProps,
  3. initMapDispatchToProps,
  4. initMergeProps,
  5. ...options
  6. }) {
  7. const mapStateToProps = initMapStateToProps(dispatch, options)
  8. const mapDispatchToProps = initMapDispatchToProps(dispatch, options)
  9. const mergeProps = initMergeProps(dispatch, options)
  10. if (process.env.NODE_ENV !== "production") {
  11. verifySubselectors(mapStateToProps, mapDispatchToProps, mergeProps, options.displayName)
  12. }
  13. const selectorFactory = options.pure
  14. ? pureFinalPropsSelectorFactory
  15. : impureFinalPropsSelectorFactory
  16. return selectorFactory(
  17. mapStateToProps,
  18. mapDispatchToProps,
  19. mergeProps,
  20. dispatch,
  21. options
  22. )
  23. }

  selectorFactory函数首先接受两个参数,dispatch和一系列的factoryOptions,通过一系列的初始化函数分别生成了mapStateToPropsmapDispatchToPropsmergeProps(初始化函数随后会详细介绍)。然后会在非生产环境下对上述三个函数进行验证(验证主要涉及到该函数是否为空和函数中是否有dependsOnOwnProps属性,这个属性随后会介绍的)。随后便是函数的重点部分,根据options.pure是否为true,选择恰当的selectorFactory,然后返回selectorFactory(...args)
  当options.purefalse时,selectorFactory的值为:impureFinalPropsSelectorFactory:

</>复制代码

  1. function impureFinalPropsSelectorFactory(
  2. mapStateToProps,
  3. mapDispatchToProps,
  4. mergeProps,
  5. dispatch
  6. ) {
  7. return function impureFinalPropsSelector(state, ownProps) {
  8. return mergeProps(
  9. mapStateToProps(state, ownProps),
  10. mapDispatchToProps(dispatch, ownProps),
  11. ownProps
  12. )
  13. }
  14. }

  我们知道,selectorFactory会返回selector函数,返回的函数会接受两个参数:stateownProps并最终返回属性传递给被包裹的组件。我们发现impureFinalPropsSelectorFactory非常的简单,只是单纯的将要求的参数传递给mapStateToPropsmapDispatchToProps,并将其结果连同ownProps一起传递给mergeProps,并将最后mergeProps的结果作为selector函数的结果。这个结果最终会传递给被包裹组件,这个函数没有什么难度而且非常符合connect函数的API。
  但我们知道在默认情况下,options.puretrue。因此selectorFactory的值为:pureFinalPropsSelectorFactory:

</>复制代码

  1. pureFinalPropsSelectorFactory(
  2. mapStateToProps,
  3. mapDispatchToProps,
  4. mergeProps,
  5. dispatch,
  6. { areStatesEqual, areOwnPropsEqual, areStatePropsEqual }
  7. ) {
  8. let hasRunAtLeastOnce = false
  9. let state
  10. let ownProps
  11. let stateProps
  12. let dispatchProps
  13. let mergedProps
  14. // ......
  15. return function pureFinalPropsSelector(nextState, nextOwnProps) {
  16. return hasRunAtLeastOnce
  17. ? handleSubsequentCalls(nextState, nextOwnProps)
  18. : handleFirstCall(nextState, nextOwnProps)
  19. }
  20. }

  函数pureFinalPropsSelectorFactory中有一个闭包变量hasRunAtLeastOnce用来判断是否是第一次调用,如果selector函数是第一次调用,selector会返回handleFirstCall(nextState, nextOwnProps)否则返回handleSubsequentCalls(nextState, nextOwnProps)

</>复制代码

  1. function handleFirstCall(firstState, firstOwnProps) {
  2. state = firstState
  3. ownProps = firstOwnProps
  4. stateProps = mapStateToProps(state, ownProps)
  5. dispatchProps = mapDispatchToProps(dispatch, ownProps)
  6. mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
  7. hasRunAtLeastOnce = true
  8. return mergedProps
  9. }

  handleFirstCall与之前的impureFinalPropsSelector相比,只是做了缓存,保存了stateownProps以及mapStateToPropsdispatchPropsmergedProps的结果值。

</>复制代码

  1. function handleSubsequentCalls(nextState, nextOwnProps) {
  2. const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps)
  3. const stateChanged = !areStatesEqual(nextState, state)
  4. state = nextState
  5. ownProps = nextOwnProps
  6. if (propsChanged && stateChanged) return handleNewPropsAndNewState()
  7. if (propsChanged) return handleNewProps()
  8. if (stateChanged) return handleNewState()
  9. return mergedProps
  10. }

  再看函数handleSubsequentCalls。其中areOwnPropsEqualareStatesEqual分别用来判断props和state现在的值与缓存的值是否相等函数。handleSubsequentCalls首先判断state、props的前后值是否有变化,然后缓存了stateownProps。如果props和state都发送改变了,返回handleNewPropsAndNewState的结果,如果props改变了,返回handleNewProps的运行结果。如果state改变,返回handleNewState运行结果,否则如果stateprops都没发生改变,说明都没有发生改变。直接返回之前缓存的mergedProps的值。
  
  handleNewPropsAndNewState定义如下:

</>复制代码

  1. function handleNewPropsAndNewState() {
  2. stateProps = mapStateToProps(state, ownProps)
  3. if (mapDispatchToProps.dependsOnOwnProps)
  4. dispatchProps = mapDispatchToProps(dispatch, ownProps)
  5. mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
  6. return mergedProps
  7. }

  我们看到,如果props和state都发送改变了,调用了handleNewPropsAndNewState,首先就是运行
mapStateToProps返回stateProps的值并缓存,其次我们会根据mapDispatchToProps.dependsOnOwnProps的值去判别是否运行mapDispatchToPropsdependsOnOwnProps的值主要是用来判别mapDispatchToProps是否依赖于ownProps的值。最终执行mergeProps函数,缓存结果并传入被包裹的组件。

</>复制代码

  1. function handleNewProps() {
  2. if (mapStateToProps.dependsOnOwnProps)
  3. stateProps = mapStateToProps(state, ownProps)
  4. if (mapDispatchToProps.dependsOnOwnProps)
  5. dispatchProps = mapDispatchToProps(dispatch, ownProps)
  6. mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
  7. return mergedProps
  8. }

  理解了handleNewPropsAndNewStatehandleNewProps将会非常简单,分别去判别statedispatchProps是否与ownProps相关。以判别是否需要重新运行mapStateToPropsmapDispatchToProps。最终将mergeProps运行的值缓存并传递给被包裹的组件。

</>复制代码

  1. function handleNewState() {
  2. const nextStateProps = mapStateToProps(state, ownProps)
  3. const statePropsChanged = !areStatePropsEqual(nextStateProps, stateProps)
  4. stateProps = nextStateProps
  5. if (statePropsChanged)
  6. mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
  7. return mergedProps
  8. }

  handleNewState用来生成新的state。根据是否state变化,选择性是否执行mergeProps,最终返回mergedProps给被包裹组件。
  
  到现在为止,其实我们已经知道了selectorFactory是与pure值挂钩的。如果puretrue的话,selectorFactory返回的selector会对stateprops等值都会缓存,然后会根据具体的场景,尽可能使得传入被包裹组件的值改动最少(即尽可能返回相同的值),其目的就是减少不必要的渲染。当purefalse值,不会做任何的缓存。
  

mapStateToProps起源

  看完了selectorFactory,我们需要去了解一下mapStateToProps是怎么来的:

</>复制代码

  1. //connect.js
  2. // initMapStateToProps会被传入 selectorFactory
  3. const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, "mapStateToProps")

  

</>复制代码

  1. //selectorFactory.js
  2. const mapStateToProps = initMapStateToProps(dispatch, options)
  3. //mapStateToProps的使用(注意这里的mapStateToProps不是传入的函数,而是init函数生成的函数):
  4. const stateProps = mapStateToProps(state, ownProps)

  我们可以看到,首先在connect.js中通过match函数取生成initMapStateToProps。然后在selectorFactory中,生成了mapStateToProps的函数,然后会在selector函数中使用mapStateToProps生成了stateProps,最后将stateProps传递给被包裹的组件。

  首先看match函数的定义:

</>复制代码

  1. function match(arg, factories, name) {
  2. for (let i = factories.length - 1; i >= 0; i--) {
  3. const result = factories[i](arg)
  4. if (result) return result
  5. }
  6. return (dispatch, options) => {
  7. throw new Error(`Invalid value of type ${typeof arg} for ${name} argument when connecting component ${options.wrappedComponentName}.`)
  8. }
  9. }

  接下来的内容相对来说会比较复杂,我们先提前梳理一下match函数的运作,其中factories是一个数组,它的实参将会是类似于mapStateToPropsFactories(数组)等值,然后args将是你自定义的mapStateToProps函数等值(比如mapStateToDispatch)。我们将会以args作为参数从后到前执行factories数组中的每一个函数,找到第一个返回不为假(类似于undefined)的函数并且我们可以保证这个函数返回的是另一个函数,其签名类似于:

</>复制代码

  1. (dispatch,options)=>{
  2. //....
  3. return ()=>{
  4. }
  5. }

这个返回的函数接受dispatch和其他选项options作为参数,最终返回一个函数供selector使用的函数 ,比如mapStateToPropsFactories一定会返回一个类似与于下面的函数:

</>复制代码

  1. (state, ownProps) =>{
  2. //......
  3. //return plain object
  4. }

这个函数将用来计算新的state传递给被包裹的组件。

  对于mapStateToProps的来源要追溯到:

</>复制代码

  1. const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, "mapStateToProps")

  
  在函数match中第一个实参是你传入connectmapStateToProps。第二个实参mapStateToPropsFactories的定义如下:

</>复制代码

  1. const mapStateToPropsFactories = [
  2. whenMapStateToPropsIsFunction,
  3. whenMapStateToPropsIsMissing
  4. ];
  5. function whenMapStateToPropsIsFunction(mapStateToProps) {
  6. return (typeof mapStateToProps === "function")
  7. ? wrapMapToPropsFunc(mapStateToProps, "mapStateToProps")
  8. : undefined
  9. }
  10. function whenMapStateToPropsIsMissing(mapStateToProps) {
  11. return (!mapStateToProps)
  12. ? wrapMapToPropsConstant(() => ({}))
  13. : undefined
  14. }

  上面的代码都不难,首先判断传入的mapStateToProps是不是类似于null,如果是执行whenMapStateToPropsIsMissing否则去执行whenMapStateToPropsIsFunction。对于whenMapStateToPropsIsMissing来说,重要的是whenMapStateToPropsIsMissing的定义:

</>复制代码

  1. function wrapMapToPropsConstant(getConstant) {
  2. return function initConstantSelector(dispatch, options) {
  3. const constant = getConstant(dispatch, options)
  4. function constantSelector() { return constant }
  5. constantSelector.dependsOnOwnProps = false
  6. return constantSelector
  7. }
  8. }

  wrapMapToPropsConstant函数接受的参数是一个函数,这个函数负责在selector返回一个常量作为props返回给被包裹组件。因为返回的总是一个常量,所以dependsOnOwnPropsfalse,表示返回给被包裹组件的值与连接到store的高阶组件接受到的props无关。
  
  那么whenMapStateToPropsIsMissing函数调用wrapMapToPropsConstant的参数是一个空函数(()=>{}),那就说明在mapStateToProps值为空(null)的时候,是不给被包裹组件传递任何的属性的。
  
  whenMapStateToPropsIsFunction的情况会比较复杂,如果传入的mapStateToProps是一个函数,那么就会调用wrapMapToPropsFunc:
  

</>复制代码

  1. function wrapMapToPropsFunc(mapToProps, methodName) {
  2. return function initProxySelector(dispatch, { displayName }) {
  3. const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
  4. return proxy.dependsOnOwnProps
  5. ? proxy.mapToProps(stateOrDispatch, ownProps)
  6. : proxy.mapToProps(stateOrDispatch)
  7. }
  8. proxy.dependsOnOwnProps = true
  9. proxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch, ownProps) {
  10. proxy.mapToProps = mapToProps
  11. proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)
  12. let props = proxy(stateOrDispatch, ownProps)
  13. if (typeof props === "function") {
  14. proxy.mapToProps = props
  15. proxy.dependsOnOwnProps = getDependsOnOwnProps(props)
  16. props = proxy(stateOrDispatch, ownProps)
  17. }
  18. if (process.env.NODE_ENV !== "production")
  19. verifyPlainObject(props, displayName, methodName)
  20. return props
  21. }
  22. return proxy
  23. }
  24. }

  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是可以返回一个函数的,如果返回的值为一个函数,这个函数将会被作为proxymapStateToProps,再次去执行proxy
  

mapDispatchToProps起源

  
  再去了解一下mapStateToProps的来源:

</>复制代码

  1. //connect.js
  2. const initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, "mapDispatchToProps")

</>复制代码

  1. //selectFactory
  2. const mapDispatchToProps = initMapDispatchToProps(dispatch, options)
  3. //使用:
  4. const dispatchProps = mapDispatchToProps(dispatch, ownProps)

  其实mapDispatchToProps是和mapStateToProps的来源非常相似,照理看mapDispatchToPropsFactories:

</>复制代码

  1. const mapDispatchToPropsFactories = [
  2. whenMapDispatchToPropsIsFunction,
  3. whenMapDispatchToPropsIsMissing,
  4. whenMapDispatchToPropsIsObject
  5. ]
  6. function whenMapDispatchToPropsIsFunction(mapDispatchToProps) {
  7. return (typeof mapDispatchToProps === "function")
  8. ? wrapMapToPropsFunc(mapDispatchToProps, "mapDispatchToProps")
  9. : undefined
  10. }
  11. function whenMapDispatchToPropsIsMissing(mapDispatchToProps) {
  12. return (!mapDispatchToProps)
  13. ? wrapMapToPropsConstant(dispatch => ({ dispatch }))
  14. : undefined
  15. }
  16. function whenMapDispatchToPropsIsObject(mapDispatchToProps) {
  17. return (mapDispatchToProps && typeof mapDispatchToProps === "object")
  18. ? wrapMapToPropsConstant(dispatch => bindActionCreators(mapDispatchToProps, dispatch))
  19. : undefined
  20. }

  如果你已经看懂了wrapMapToPropsConstantwrapMapToPropsFunc的函数的话,mapDispatchToPropsFactories也就不难了。如果传入的mapStateToProps的值是一个对象的话,会调用whenMapDispatchToPropsIsObject。继而调用了wrapMapToPropsConstant并传入的参数是函数:dispatch => bindActionCreators(mapDispatchToProps, dispatch)。根据我们之前经验,那么传递给被包裹的组件的属性将是:bindActionCreators(mapDispatchToProps, dispatch)的运行结果,即被dispatch包裹的action

  如果没有传入mapDispatchToProps函数的话,调用whenMapDispatchToPropsIsMissing。传入函数wrapMapToPropsConstant的参数为:dispatch => ({ dispatch }),那么被包裹的组件接受的参数即是storedispatch方法。
  
  如果传入的mapDispatchToProps是一个函数,调用whenMapDispatchToPropsIsFunction函数。从而调用wrapMapToPropsFunc(mapDispatchToProps, "mapDispatchToProps")。运行的原理与运行wrapMapToPropsFunc(mapStateToProps, "mapStateToProps")基本相同,可以参照之前。
  

mergeProps起源

</>复制代码

  1. //connect.js
  2. const initMergeProps = match(mergeProps, mergePropsFactories, "mergeProps")

</>复制代码

  1. //selectorFactory
  2. const mergeProps = initMergeProps(dispatch, options)
  3. //使用
  4. mergedProps = mergeProps(stateProps, dispatchProps, ownProps)

  
  还是先看一下mergePropsFactories是怎么定义的:
  

</>复制代码

  1. const mergePropsFactories = [
  2. whenMergePropsIsFunction,
  3. whenMergePropsIsOmitted
  4. ]
  5. function whenMergePropsIsFunction(mergeProps) {
  6. return (typeof mergeProps === "function")
  7. ? wrapMergePropsFunc(mergeProps)
  8. : undefined
  9. }
  10. function whenMergePropsIsOmitted(mergeProps) {
  11. return (!mergeProps)
  12. ? () => defaultMergeProps
  13. : undefined
  14. }

  如果你没有传入mapStateToProps函数,那么调用函数whenMergePropsIsOmitted()。到最后margedProps函数即是defaultMergeProps,defaultMergeProps的定义为:

</>复制代码

  1. function defaultMergeProps(stateProps, dispatchProps, ownProps) {
  2. return { ...ownProps, ...stateProps, ...dispatchProps }
  3. }

  如果你传入了mapStateToProps函数,调用函数whenMergePropsIsFunction(),调用了wrapMergePropsFunc(mergeProps),其中参数mergeProps即是你所传入的mergeProps:

</>复制代码

  1. function wrapMergePropsFunc(mergeProps) {
  2. return function initMergePropsProxy(dispatch, { displayName, pure, areMergedPropsEqual }) {
  3. let hasRunOnce = false
  4. let mergedProps
  5. return function mergePropsProxy(stateProps, dispatchProps, ownProps) {
  6. const nextMergedProps = mergeProps(stateProps, dispatchProps, ownProps)
  7. if (hasRunOnce) {
  8. if (!pure || !areMergedPropsEqual(nextMergedProps, mergedProps))
  9. mergedProps = nextMergedProps
  10. } else {
  11. hasRunOnce = true
  12. mergedProps = nextMergedProps
  13. if (process.env.NODE_ENV !== "production")
  14. verifyPlainObject(mergedProps, displayName, "mergeProps")
  15. }
  16. return mergedProps
  17. }
  18. }
  19. }

  wrapMergePropsFunc中涉及到性能优化,首先wrapMergePropsFunc返回一个初始mergeProps的函数(mergePropsProxy)。函数mergePropsProxy闭包一个变量hasRunOnce来记录mergeProps运行次数,在mergeProps第一次运行时,会保存第一次传入被包裹组件的的props,再以后的运行过程中,如果你传入的参数puretrue并且前后的mergedProps值不同时(比较函数你可以自定义)才会传入新的属性,否则将传入之前的缓存值,以此来优化不必要的渲染。

  到此为止,我们基本已经在代码层面讲完了connect函数的原理,文章很长,有的地方可能相对比较难理解,建议大家都可以去从整体上看看react-redux的源码。react-redux源码解读系列接下来会以其他的角度去分析react-redux,欢迎大家继续关注。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/84307.html

相关文章

  • 2017-07-26 前端日报

    摘要:前端日报精选庖丁解牛二深入解析模板字符串八段代码彻底掌握高效压缩文件束的体积译才不是什么黑魔法呢发布中文译数据结构栈与队列疯狂的技术宅中自定义指令修仙之路更好的异步解决方案发布同步代码书写异步情怀有赞前端团队位运算,也许 2017-07-26 前端日报 精选 庖丁解牛React-Redux(二): connect深入解析 ES6:模板字符串_ES6八段代码彻底掌握Promise高效压缩...

    Binguner 评论0 收藏0
  • 2017-07-10 前端日报

    摘要:前端日报精选入门指南入口,输出,加载器和插件中数据类型转换让我印象深刻的面试题大话大前端时代一与的组件化庖丁解牛一发布中文第期手把手教你用管理状态上个快速编程技巧众成翻译中执行顺序组件解耦之道众成翻译组件模型启示录有个梨作 2017-07-10 前端日报 精选 Webpack入门指南: 入口,输出,加载器和插件JavaScript中数据类型转换让我印象深刻的javascript面试题大...

    Heier 评论0 收藏0
  • 快速理解react-redux

    摘要:和的结合简述相信很多前端开发者都听说或使用过,我曾写过一篇关于快速理解的文章,虽说是快速理解,但实际上更应该叫做复习吧。它通过高阶函数,纯函数使我们在编写组件时完全不用接触相关内容,只通过将组件和数据连接起来即可。 react-redux react和redux的结合 简述 相信很多前端开发者都听说或使用过react-redux,我曾写过一篇关于快速理解redux的文章,虽说是快...

    MoAir 评论0 收藏0
  • 手挽手带你学React:四档(下篇)一步一步学会react-redux

    摘要:手挽手带你学入门四档用人话教你,理解架构,以及运用在中。学完这一章,你就可以开始自己的项目了。结合搭建基础环境我们上一章讲过了的原理,内部是有一个的,只有才可以控制它变化。 手挽手带你学React入门四档,用人话教你react-redux,理解redux架构,以及运用在react中。学完这一章,你就可以开始自己的react项目了。 视频教程 上一篇我们自己实现了Redux,这一篇我们来...

    FullStackDeveloper 评论0 收藏0
  • 大话React-Redux

    摘要:一组件将所有组件分成两大类组件和容器组件。前者负责与外部的通信,将数据传给后者,由后者渲染出视图。也就是说,用户负责视觉层,状态管理则是全部交给它。前者需要从计算得到,后者需要向外发出。最后,生成对象,并使用在根组件外面包一层。 一、UI 组件 React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container c...

    huangjinnan 评论0 收藏0

发表评论

0条评论

Taste

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<