react-redux redux源码心得

395 阅读3分钟

react-redux/src/connect/connect.js

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

执行match,调用如下mapStateToPropsFactories,完成“业务mapToPorps”初始化操作(例如{}时补齐操作)

mapStateToPropsFactories = [
  whenMapStateToPropsIsFunction,
  whenMapStateToPropsIsMissing
]
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}.`)
  }
}
其中一个分支:
export function whenMapStateToPropsIsFunction(mapStateToProps) {
  return (typeof mapStateToProps === 'function')
    ? wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps')
    : undefined
}
export function wrapMapToPropsFunc(mapToProps, methodName) {
  return function initProxySelector(dispatch, { displayName }) {
    const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
      // 第一次dependsOnOwnProps为true,调用mapToProp为detectFactoryAndVerify(stateOrDispatch, ownProps)
      // 第二次mapToProp为“业务代码中的mapStateToProps”
      return proxy.dependsOnOwnProps
        ? proxy.mapToProps(stateOrDispatch, ownProps)
        : proxy.mapToProps(stateOrDispatch)
    }

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

    proxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch, ownProps) {
      // mapToProps被重新赋值为“业务代码中的mapStateToProps”
      proxy.mapToProps = mapToProps
      proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)
      let props = proxy(stateOrDispatch, ownProps)

      if (typeof props === 'function') {
        // mapToProps被重新赋值为“业务代码中的mapStateToProps执行后的返回值”
        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
  }
}

=> initMapDispatchToProps = initProxySelector(dispatch, { displayName })

react-redux/src/components/connectAdvanced.js

initSelector

initSelector() {
    // 1、生成sourceSelector
    const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions)
    // 2、包装selector
    this.selector = makeSelectorStateful(sourceSelector, this.store)
    // 3、state发生变化的时候调用selector,最终赋值selector.props = nextProps
    this.selector.run(this.props)
}

1、生成sourceSelector connectAdvanced方法中的selectorFactory = finalPropsSelectorFactory

export default function finalPropsSelectorFactory(dispatch, {
  initMapStateToProps,
  initMapDispatchToProps,
  initMergeProps,
  ...options
}) {
  // 返回proxy
  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 = pureFinalPropsSelectorFactory

export function pureFinalPropsSelectorFactory(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps,
  dispatch,
  { areStatesEqual, areOwnPropsEqual, areStatePropsEqual }
) {
  let hasRunAtLeastOnce = false
  let state
  let ownProps
  let stateProps
  let dispatchProps
  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)

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

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

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

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

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

  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)
    const stateChanged = !areStatesEqual(nextState, 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)
  }
}

const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions) => sourceSelector = handleSubsequentCalls(nextState, nextOwnProps) 或者 handleFirstCall(nextState, nextOwnProps)

2、包装selector(错误处理+传递函数) this.selector = makeSelectorStateful(sourceSelector, this.store)

function makeSelectorStateful(sourceSelector, store) {
  // wrap the selector in an object that tracks its results between runs.
  const selector = {
    run: function runComponentSelector(props) {
      try {
        const nextProps = sourceSelector(store.getState(), props)
        if (nextProps !== selector.props || selector.error) {
          selector.shouldComponentUpdate = true
          selector.props = nextProps
          selector.error = null
        }
      } catch (error) {
        selector.shouldComponentUpdate = true
        selector.error = error
      }
    }
  }

  return selector
}

=> this.selector = makeSelectorStateful中的selector

3、state发生变化的时候调用selector,最终赋值selector.props = nextProps selector.run 执行const nextProps = sourceSelector(store.getState(), props) 其中一个分支:

function handleFirstCall(firstState, firstOwnProps) {
    state = firstState
    ownProps = firstOwnProps
    // mapStateToProps是上面的Proxy
    stateProps = mapStateToProps(state, ownProps)
    dispatchProps = mapDispatchToProps(dispatch, ownProps)
    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    hasRunAtLeastOnce = true
    // 返回所有的props对象(包括status, dispatch)
    return mergedProps
  }

=>handleFirstCall(firstState, firstOwnProps) 返回的mergeProps最终传递给connect()(WrappedComponent)中的WrappedComponent

  • react-redux/src/components/connectAdvanced/Connect.render
render() {
  return createElement(WrappedComponent, this.addExtraProps(selector.props))
}

initSubscription

  • react-redux/src/utils/Subscription.js
addNestedSub(listener) {
    this.trySubscribe()
    // 下层Container的onStatechange存储在react-redux中
    return this.listeners.subscribe(listener)
}
trySubscribe() {
    if (!this.unsubscribe) {
    // 没有父级观察者的话,直接监听store change
    // 有的话,添到父级下面,由父级传递变化
      this.unsubscribe = this.parentSub
        ? this.parentSub.addNestedSub(this.onStateChange)
        // container的onStatechange存储在redux中
        : this.store.subscribe(this.onStateChange)
 
      this.listeners = createListenerCollection()
    }
}

react-redux调用store.subscribe(redux通过createstore从生成的store),只对顶层Container直接监听了state change,下层Container都是内部传递通知的.

  • react-redux/src/components/connectAdvanced/Connect.onStateChange
onStateChange() {
  // state change时重新计算props
  this.selector.run(this.props)

  // 当前组件不用更新的话,通知下方container检查更新
  // 要更新的话,setState空对象强制更新,延后通知到didUpdate
  if (!this.selector.shouldComponentUpdate) {
    this.notifyNestedSubs()
  } else {
    this.componentDidUpdate = this.notifyNestedSubsOnComponentDidUpdate
    // 通知Container下方的view更新
//!!! 这里是把redux与react连接起来的关键
    this.setState(dummyState)
  }
}

最重要的那个setState就在这里,dispatch action后视图更新的秘密是这样:

1.dispatch action
2.redux计算reducer得到newState
3.redux触发state change(调用之前通过store.subscribe注册的state变化监听器)
4.react-redux顶层Container的onStateChange触发
  1.重新计算props
  2.比较新值和缓存值,看props变了没,要不要更新
  3.要的话通过setState({})强制react更新
  4.通知下方的subscription,触发下方关注state change的Container的onStateChange,检查是否需要更新view
  • react-redux/src/components/connectAdvanced/Connect.render
render() {
  return createElement(WrappedComponent, this.addExtraProps(selector.props))
}

根据WrappedComponent需要的state字段,造一份props,通过React.createElement注入进去。ContainerInstance.setState({})时,这个render函数被重新调用,新的props被注入到view,view will receive props…视图更新就真正开始了

参考: www.ayqy.net/blog/react-…