React Redux: 從文檔看源碼 - Connect工具類篇(2)

注:這篇文章只是講解React Redux這一層,並不包含Redux部分。Redux有計劃去學習,等之後學習了Redux源碼之後再作分析
注:代碼基於如今(2016.12.29)React Redux的最新版本(5.0.1)segmentfault

Connect工具類篇(1)app

Connect工具類篇(2)

verifySubselectors.js

這裏有四個參數:工具

  1. mapStateToProps, mapDispatchToProps, mergeProps都是通過mapDispatchToProps, mapStateToProps, mergeProps封裝過的學習

  2. displayName是option裏面傳入的
    主要作的是檢查傳入的mapStateToProps,mapDispatchToProps,mergeProps是否存在,因爲這裏的對象是通過幾個map方法封裝過的,因此不存在就說明開發傳入的值是錯誤的。同時檢查是否有dependsOnOwnProps這個屬性,若是沒有給一個warning.spa

import warning from '../utils/warning'

function verify(selector, methodName, displayName) {
  if (!selector) {
    throw new Error(`Unexpected value for ${methodName} in ${displayName}.`)

  } else if (methodName === 'mapStateToProps' || methodName === 'mapDispatchToProps') { //只檢查mapStateToProps和mapDispatchToProps, 由於mergeProps方法不須要
    if (!selector.hasOwnProperty('dependsOnOwnProps')) {
      warning(
        `The selector for ${methodName} of ${displayName} did not specify a value for dependsOnOwnProps.`
      )
    }
  }
}

export default function verifySubselectors(mapStateToProps, mapDispatchToProps, mergeProps, displayName) {
  verify(mapStateToProps, 'mapStateToProps', displayName)
  verify(mapDispatchToProps, 'mapDispatchToProps', displayName)
  verify(mergeProps, 'mergeProps', displayName)
}

selectorFactory.js

這裏主要負責獲取處理過的mapStateToProps, mapDispatchToProps, mergeProps和傳入的options,來進行props的合併,最後返回合併後的結果。其中,當pure爲true的時候,會對props進行存儲,便於下一次比較,若是經過比較兩個相同,那麼就不改變props對象,減小沒必要要的re-render。code

在connectAdvanced.js裏面看到這麼一段註釋:對象

selectoryFactory方法返回的是一個function,這個function的做用是根據Redux Store state, props和dispatch計算新的props. 在connectAdvanced中會提供dispatch給selectorFactory,以便selectorFactory能夠對actionCreator進行綁定。connectAdvanced獲取的option配置會直接被傳給selectorFactory,其中就包含了displayName和wrappedComponent(其實還有一些對比的方法在裏面)。ci

selectorFactory負責記錄全部的狀態(props, store state, dispatch, mergedProps),以便在部分狀態發生改變,而不影響組件渲染的時候,能夠避免沒必要要的渲染。開發

finalPropsSelectorFactory

export default function 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
  )
}

這裏只是給mapStateToProps, mapDispatchToProps, mergeProps傳入dispatch和options對象,而後根據pure的值傳給不一樣的方法進行處理。get

若是pure是true, 那麼selectorFactory返回的selector會負責存儲最後結果。若是結果沒有發生改變,那麼connectAdvanced的shouldComponentUpdate就會返回false。

若是pure是false, 那麼selector永遠會返回一個新的對象,同時shouldComponentUpdate永遠都返回true

這個factory方法會返回一個function,接收Redux Store和ownProps做爲參數。做用是,每次store或ownProps發生改變之後,調用這個返回的function,獲取更新後的最終props。

impureFinalPropsSelectorFactory

export function impureFinalPropsSelectorFactory(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps,
  dispatch
) {
  return function impureFinalPropsSelector(state, ownProps) {
    return mergeProps(
      mapStateToProps(state, ownProps),
      mapDispatchToProps(dispatch, ownProps),
      ownProps
    )
  }
}

根據pure等於false的狀況,這裏會永遠返回一個新的對象。存粹的、不加任何判斷的調用mergeProps對幾個props的結構進行合併。

這裏返回的值的格式是:(state, ownProps)=>final props

pureFinalPropsSelectorFactory

export function pureFinalPropsSelectorFactory(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps,
  dispatch,
  { areStatesEqual, areOwnPropsEqual, areStatePropsEqual }
) {
  let hasRunAtLeastOnce = false // 是不是第一次調用,第一次調用不須要作是否改變的檢查
  let state // 記憶上一次的state
  let ownProps // 記憶上一次的ownProps
  let stateProps // 記憶mapStateToProps返回的props
  let dispatchProps // 記憶mapDispatchToProps返回的props
  let mergedProps // 記憶最後合併後的結果

  // 第一次調用的時候,純粹記住全部的結果
  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
  }

  // 當兩個都發生改變。。。
  function handleNewPropsAndNewState() {
    stateProps = mapStateToProps(state, ownProps) // ownProps發生了改變,確定須要調用獲取新的props

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

    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    return mergedProps
  }

  // 若是隻有父組件傳入的props發生了改變,那麼須要根據dependsOnOwnProps來進行更新
  function handleNewProps() {
    if (mapStateToProps.dependsOnOwnProps)
      stateProps = mapStateToProps(state, ownProps)

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

    // 因爲ownProps發生了改變,因此不須要進行檢查,直接調用mergeProps方法
    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    return mergedProps
  }

  // 若是隻有Redux store state發生了改變,那麼只用更新mapStateToProps的返回值,由於dispatchProps和Redux State無關
  function handleNewState() {
    const nextStateProps = mapStateToProps(state, ownProps)
    const statePropsChanged = !areStatePropsEqual(nextStateProps, stateProps)
    stateProps = nextStateProps
    
    if (statePropsChanged)
      mergedProps = mergeProps(stateProps, dispatchProps, ownProps)

    return mergedProps
  }

  // 除第一次調用外,每次都須要對各類結果進行檢查,而後記錄必要的結果
  function handleSubsequentCalls(nextState, nextOwnProps) {
    const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps) // 檢查ownProps是否發生改變
    const stateChanged = !areStatesEqual(nextState, state) // 檢查Redux store state是否發生改變
    state = nextState
    ownProps = nextOwnProps

    // 根據改變的不一樣,調用不一樣的方法。減小沒必要要的運算
    if (propsChanged && stateChanged) return handleNewPropsAndNewState()
    if (propsChanged) return handleNewProps()
    if (stateChanged) return handleNewState()
    return mergedProps
  }

  return function pureFinalPropsSelector(nextState, nextOwnProps) {
    return hasRunAtLeastOnce
      ? handleSubsequentCalls(nextState, nextOwnProps)
      : handleFirstCall(nextState, nextOwnProps)
  }
}

當pure等於true的時候,須要作出各類檢查來斷定是否須要調用方法,來獲取新的props.

  1. 當Redux state發生改變,ownProps沒變的時候
    1) 因爲mapDispatchToProps並無基於Redux State,因此dispatchProps是須要進行更新的。2) 而mapStateToProps是基於Redux State的,因此須要調用mapStateToProps方法或許"新的"stateProps。因爲State發生改變,並不必定會形成返回結果的改變,因此須要根據檢查後的結果來斷定是否調用mergeProps方法。

  2. 當OwnProps發生改變,Redux State沒有改變的時候
    1) 因爲mapDispatchToProps和mapStateToProps均可能基於ownProps,因此須要根據dependsOnOwnProps屬性來檢查,判斷是否須要調用方法進行更新。2) ownProps做爲mergeProps的一個必要參數,因此不須要作任何判斷,mergePorps必須被調用

  3. 當Redux Store, OwnProps都發生了改變
    綜合以前的兩點,mapStateToProps必須調用,mapDispatchToProps根據dependsOnOwnProps屬性調用,mergeProps必須調用

一點總結:

  1. 在connect定義的時候,通常儘可能使用pure:true的狀況(默認值),由於在這種狀況下,會對props進行差異檢查。若是沒有改變,就不會去調用connectAdvanced組件去更新。若是內部組件同時根據 父組件傳入的propsRedux store的其餘狀態進行更新渲染,那麼pure必須是false。

  2. option中的areStatesEqual(默認值爲===),areOwnPropsEqual(默認值爲shallowEqual), areStatePropsEqual(默認值爲shallowEqual), areMergedPropsEqual(默認值爲shallowEqual),能夠根據須要來修改這幾個參數,當pure爲true的時候,檢查更多沒必要要的re-render

相關文章
相關標籤/搜索