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

請叫我王磊同學發表於2019-03-04

connect API

  上篇文章庖丁解牛React-Redux(一): connectAdvanced介紹了react-redux的ProviderconnectAdvanced幾個重要API的原理,其中connectAdvancedconnect函式的基礎,這篇文章將主要介紹connect函式的原理。之前沒有閱讀過connectAdvanced最好提前閱讀一下這篇文章。之前的文章有讀者反映看起來比較晦澀,所以我準備隨後會出一篇關於類似圖解connectAdvanced的文章,不講程式碼,主要從原理的方面詮釋connectAdvanced。再次做個廣告,歡迎大家關注我的掘金賬號和我的部落格

  最開始我們還是來介紹一下connect函式:
  

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])複製程式碼

  將React元件連線到Redux store,connect函式是connectAdvanced的正面,為大多數常見場景提供了易於使用的API。connect函式並不會修改傳遞的元件,相反,它會返回一個新的,連線到store的元件類。

引數:

  • mapStateToProps(state, [ownProps]): stateProps:
    如果這個引數被傳遞,返回新的元件將會訂閱Redux的store的更新(update)。這意味著任何時刻store更新,mapStateToProps將會被呼叫。mapStateToProps必須返回一個純物件(plain object),這個物件將會合並進元件的屬性(props)。如果你不想訂閱store的更新,可以給mapStateToProps引數傳遞null或者undefined

    如果你的mapStateToProps函式被宣告接受兩個引數,mapStateToProps在呼叫時第一個引數是store state,傳遞給連線元件(connected component)的屬性將會被作為第二個引數。如果連線元件接受到新的props(淺比較),mapStateToProps也會再次呼叫。

注意: 在一些更高階的情況下,你需要更好的控制渲染的效能,mapStateToProps可以返回一個函式。這種場景下,返回的函式將會被作為特定元件例項的mapStateProps()函式。這允許你可以對每個例項快取。但大部分應用用不到。

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屬性。

注意: 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的程式碼如下:

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, {
      methodName: `connect`,
      getDisplayName: name => `Connect(${name})`,
      shouldHandleStateChanges: Boolean(mapStateToProps),
      initMapStateToProps,
      initMapDispatchToProps,
      initMergeProps,
      pure,
      areStatesEqual,
      areOwnPropsEqual,
      areStatePropsEqual,
      areMergedPropsEqual,
      ...extraOptions
    })
  }
}
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的函式簽名為:

selectorFactory(dispatch, factoryOptions): selector(state, ownProps): props (Function)複製程式碼

  我們來看看reduxselectorFactory是怎麼定義的:
 

const selectorFactory = finalPropsSelectorFactory(dispatch, {
  initMapStateToProps,
  initMapDispatchToProps,
  initMergeProps,
  ...options
}) {
  const mapStateToProps = initMapStateToProps(dispatch, options)
  const mapDispatchToProps = initMapDispatchToProps(dispatch, options)
  const mergeProps = initMergeProps(dispatch, options)

  if (process.env.NODE_ENV !== `production`) {
    verifySubselectors(mapStateToProps, mapDispatchToProps, mergeProps, options.displayName)
  }

  const selectorFactory = options.pure
    ? pureFinalPropsSelectorFactory
    : impureFinalPropsSelectorFactory

  return selectorFactory(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps,
    dispatch,
    options
  )
}複製程式碼

  selectorFactory函式首先接受兩個引數,dispatch和一系列的factoryOptions,通過一系列的初始化函式分別生成了mapStateToPropsmapDispatchToPropsmergeProps(初始化函式隨後會詳細介紹)。然後會在非生產環境下對上述三個函式進行驗證(驗證主要涉及到該函式是否為空和函式中是否有dependsOnOwnProps屬性,這個屬性隨後會介紹的)。隨後便是函式的重點部分,根據options.pure是否為true,選擇恰當的selectorFactory,然後返回selectorFactory(...args)
  當options.purefalse時,selectorFactory的值為:impureFinalPropsSelectorFactory:

function impureFinalPropsSelectorFactory(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps,
  dispatch
) {
  return function impureFinalPropsSelector(state, ownProps) {
    return mergeProps(
      mapStateToProps(state, ownProps),
      mapDispatchToProps(dispatch, ownProps),
      ownProps
    )
  }
}複製程式碼

  我們知道,selectorFactory會返回selector函式,返回的函式會接受兩個引數:stateownProps並最終返回屬性傳遞給被包裹的元件。我們發現impureFinalPropsSelectorFactory非常的簡單,只是單純的將要求的引數傳遞給mapStateToPropsmapDispatchToProps,並將其結果連同ownProps一起傳遞給mergeProps,並將最後mergeProps的結果作為selector函式的結果。這個結果最終會傳遞給被包裹元件,這個函式沒有什麼難度而且非常符合connect函式的API。
  但我們知道在預設情況下,options.puretrue。因此selectorFactory的值為:pureFinalPropsSelectorFactory:

pureFinalPropsSelectorFactory(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps,
  dispatch,
  { areStatesEqual, areOwnPropsEqual, areStatePropsEqual }
) {
  let hasRunAtLeastOnce = false
  let state
  let ownProps
  let stateProps
  let dispatchProps
  let mergedProps

  // ......    
  return function pureFinalPropsSelector(nextState, nextOwnProps) {
    return hasRunAtLeastOnce
      ? handleSubsequentCalls(nextState, nextOwnProps)
      : handleFirstCall(nextState, nextOwnProps)
  }
}複製程式碼

  函式pureFinalPropsSelectorFactory中有一個閉包變數hasRunAtLeastOnce用來判斷是否是第一次呼叫,如果selector函式是第一次呼叫,selector會返回handleFirstCall(nextState, nextOwnProps)否則返回handleSubsequentCalls(nextState, nextOwnProps)

function handleFirstCall(firstState, firstOwnProps) {
    state = firstState
    ownProps = firstOwnProps
    stateProps = mapStateToProps(state, ownProps)
    dispatchProps = mapDispatchToProps(dispatch, ownProps)
    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    hasRunAtLeastOnce = true
    return mergedProps
}複製程式碼

  handleFirstCall與之前的impureFinalPropsSelector相比,只是做了快取,儲存了stateownProps以及mapStateToPropsdispatchPropsmergedProps的結果值。

function handleSubsequentCalls(nextState, nextOwnProps) {
    const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps)
    const stateChanged = !areStatesEqual(nextState, state)
    state = nextState
    ownProps = nextOwnProps

    if (propsChanged && stateChanged) return handleNewPropsAndNewState()
    if (propsChanged) return handleNewProps()
    if (stateChanged) return handleNewState()
    return mergedProps
}複製程式碼

  再看函式handleSubsequentCalls。其中areOwnPropsEqualareStatesEqual分別用來判斷props和state現在的值與快取的值是否相等函式。handleSubsequentCalls首先判斷state、props的前後值是否有變化,然後快取了stateownProps。如果props和state都傳送改變了,返回handleNewPropsAndNewState的結果,如果props改變了,返回handleNewProps的執行結果。如果state改變,返回handleNewState執行結果,否則如果stateprops都沒發生改變,說明都沒有發生改變。直接返回之前快取的mergedProps的值。
  
  handleNewPropsAndNewState定義如下:

function handleNewPropsAndNewState() {
    stateProps = mapStateToProps(state, ownProps)

    if (mapDispatchToProps.dependsOnOwnProps)
      dispatchProps = mapDispatchToProps(dispatch, ownProps)

    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    return mergedProps
}複製程式碼

  我們看到,如果props和state都傳送改變了,呼叫了handleNewPropsAndNewState,首先就是執行
mapStateToProps返回stateProps的值並快取,其次我們會根據mapDispatchToProps.dependsOnOwnProps的值去判別是否執行mapDispatchToPropsdependsOnOwnProps的值主要是用來判別mapDispatchToProps是否依賴於ownProps的值。最終執行mergeProps函式,快取結果並傳入被包裹的元件。

function handleNewProps() {
    if (mapStateToProps.dependsOnOwnProps)
      stateProps = mapStateToProps(state, ownProps)

    if (mapDispatchToProps.dependsOnOwnProps)
      dispatchProps = mapDispatchToProps(dispatch, ownProps)

    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    return mergedProps
}複製程式碼

  理解了handleNewPropsAndNewStatehandleNewProps將會非常簡單,分別去判別statedispatchProps是否與ownProps相關。以判別是否需要重新執行mapStateToPropsmapDispatchToProps。最終將mergeProps執行的值快取並傳遞給被包裹的元件。

function handleNewState() {
    const nextStateProps = mapStateToProps(state, ownProps)
    const statePropsChanged = !areStatePropsEqual(nextStateProps, stateProps)
    stateProps = nextStateProps
    if (statePropsChanged)
      mergedProps = mergeProps(stateProps, dispatchProps, ownProps)

    return mergedProps
}複製程式碼

  handleNewState用來生成新的state。根據是否state變化,選擇性是否執行mergeProps,最終返回mergedProps給被包裹元件。
  
  到現在為止,其實我們已經知道了selectorFactory是與pure值掛鉤的。如果puretrue的話,selectorFactory返回的selector會對stateprops等值都會快取,然後會根據具體的場景,儘可能使得傳入被包裹元件的值改動最少(即儘可能返回相同的值),其目的就是減少不必要的渲染。當purefalse值,不會做任何的快取。
  

mapStateToProps起源

  看完了selectorFactory,我們需要去了解一下mapStateToProps是怎麼來的:

//connect.js
// initMapStateToProps會被傳入 selectorFactory
const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, `mapStateToProps`)複製程式碼

  

//selectorFactory.js
const mapStateToProps = initMapStateToProps(dispatch, options)
//mapStateToProps的使用(注意這裡的mapStateToProps不是傳入的函式,而是init函式生成的函式):
const stateProps = mapStateToProps(state, ownProps)複製程式碼

  我們可以看到,首先在connect.js中通過match函式取生成initMapStateToProps。然後在selectorFactory中,生成了mapStateToProps的函式,然後會在selector函式中使用mapStateToProps生成了stateProps,最後將stateProps傳遞給被包裹的元件。

  首先看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}.`)
  }
}複製程式碼

  接下來的內容相對來說會比較複雜,我們先提前梳理一下match函式的運作,其中factories是一個陣列,它的實參將會是類似於mapStateToPropsFactories(陣列)等值,然後args將是你自定義的mapStateToProps函式等值(比如mapStateToDispatch)。我們將會以args作為引數從後到前執行factories陣列中的每一個函式,找到第一個返回不為假(類似於undefined)的函式並且我們可以保證這個函式返回的是另一個函式,其簽名類似於:

(dispatch,options)=>{
    //....
    return ()=>{
    }
}複製程式碼

這個返回的函式接受dispatch和其他選項options作為引數,最終返回一個函式供selector使用的函式 ,比如mapStateToPropsFactories一定會返回一個類似與於下面的函式:

(state, ownProps) =>{
    //......
    //return plain object
}複製程式碼

這個函式將用來計算新的state傳遞給被包裹的元件。

  對於mapStateToProps的來源要追溯到:

const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, `mapStateToProps`)複製程式碼

  
  在函式match中第一個實參是你傳入connectmapStateToProps。第二個實參mapStateToPropsFactories的定義如下:

const mapStateToPropsFactories = [
  whenMapStateToPropsIsFunction,
  whenMapStateToPropsIsMissing
];

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

function whenMapStateToPropsIsMissing(mapStateToProps) {
  return (!mapStateToProps)
    ? wrapMapToPropsConstant(() => ({}))
    : undefined
}複製程式碼

  上面的程式碼都不難,首先判斷傳入的mapStateToProps是不是類似於null,如果是執行whenMapStateToPropsIsMissing否則去執行whenMapStateToPropsIsFunction。對於whenMapStateToPropsIsMissing來說,重要的是whenMapStateToPropsIsMissing的定義:

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

    function constantSelector() { return constant }
    constantSelector.dependsOnOwnProps = false 
    return constantSelector
  }
}複製程式碼

  wrapMapToPropsConstant函式接受的引數是一個函式,這個函式負責在selector返回一個常量作為props返回給被包裹元件。因為返回的總是一個常量,所以dependsOnOwnPropsfalse,表示返回給被包裹元件的值與連線到store的高階元件接受到的props無關。
  
  那麼whenMapStateToPropsIsMissing函式呼叫wrapMapToPropsConstant的引數是一個空函式(()=>{}),那就說明在mapStateToProps值為空(null)的時候,是不給被包裹元件傳遞任何的屬性的。
  
  whenMapStateToPropsIsFunction的情況會比較複雜,如果傳入的mapStateToProps是一個函式,那麼就會呼叫wrapMapToPropsFunc:
  

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

    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是可以返回一個函式的,如果返回的值為一個函式,這個函式將會被作為proxymapStateToProps,再次去執行proxy
  

mapDispatchToProps起源

  
  再去了解一下mapStateToProps的來源:

//connect.js
const initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, `mapDispatchToProps`)複製程式碼
//selectFactory
const mapDispatchToProps = initMapDispatchToProps(dispatch, options)
//使用:
const dispatchProps = mapDispatchToProps(dispatch, ownProps)複製程式碼

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

const mapDispatchToPropsFactories =  [
  whenMapDispatchToPropsIsFunction,
  whenMapDispatchToPropsIsMissing,
  whenMapDispatchToPropsIsObject
]

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

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

function whenMapDispatchToPropsIsObject(mapDispatchToProps) {
  return (mapDispatchToProps && typeof mapDispatchToProps === `object`)
    ? wrapMapToPropsConstant(dispatch => bindActionCreators(mapDispatchToProps, dispatch))
    : undefined
}複製程式碼

  如果你已經看懂了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起源

//connect.js
const initMergeProps = match(mergeProps, mergePropsFactories, `mergeProps`)複製程式碼
//selectorFactory
const mergeProps = initMergeProps(dispatch, options)
//使用
mergedProps = mergeProps(stateProps, dispatchProps, ownProps)複製程式碼

  
  還是先看一下mergePropsFactories是怎麼定義的:
  

const mergePropsFactories = [
  whenMergePropsIsFunction,
  whenMergePropsIsOmitted
]

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

function whenMergePropsIsOmitted(mergeProps) {
  return (!mergeProps)
    ? () => defaultMergeProps
    : undefined
}複製程式碼

  如果你沒有傳入mapStateToProps函式,那麼呼叫函式whenMergePropsIsOmitted()。到最後margedProps函式即是defaultMergeProps,defaultMergeProps的定義為:

function defaultMergeProps(stateProps, dispatchProps, ownProps) {
  return { ...ownProps, ...stateProps, ...dispatchProps }
}複製程式碼

  如果你傳入了mapStateToProps函式,呼叫函式whenMergePropsIsFunction(),呼叫了wrapMergePropsFunc(mergeProps),其中引數mergeProps即是你所傳入的mergeProps:

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,再以後的執行過程中,如果你傳入的引數puretrue並且前後的mergedProps值不同時(比較函式你可以自定義)才會傳入新的屬性,否則將傳入之前的快取值,以此來優化不必要的渲染。

  到此為止,我們基本已經在程式碼層面講完了connect函式的原理,文章很長,有的地方可能相對比較難理解,建議大家都可以去從整體上看看react-redux的原始碼。react-redux原始碼解讀系列接下來會以其他的角度去分析react-redux,歡迎大家繼續關注。

相關文章