React redux源码解析

前言

React redux是一个将React与Redux关联的库,它由redux官方出品。

React redux在内部进行了各种解耦,因此扩展性强,并且通过缓存数据减少了不必要的re-render,具有很高的效率。

Selector

Selector是一个函数,由selectorFactory得到。通过调用传入SelectorFactorymapStateToPropsmapDispatchToPropsmergeProps,得到一个finalProps,并将它作为props注入到被connect的组件中。

mapStateToPropsFactoriesmapDispatchToPropsFactoriesmergePropsFactories三个数组,每个数组里包含了若干个工厂方法,若match方法返回一个函数说明当前的mapStateToPropsmapDispatchToPropsmergeProps匹配上了数组中的某个工厂方法,这个匹配上的工厂方法会成为initMapStateToPropsinitMapDispatchToPropsinitMergeProps

下面是match的实现

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

    // 匹配上了就返回这个initXXXXProps
    if (result) return result
  }

  // 一个factory都匹配不上的时候,说明传入的`mapStateToProps`、`mapDispatchToProps`、`mergeProps`类型错误
  return (dispatch, options) => {
    throw new Error(`Invalid value of type ${typeof arg} for ${name} argument when connecting component ${options.wrappedComponentName}.`)
  }

这个match被这样使用

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

在看三个工厂函数数组的实现之前,先看一下wrapMapToPropsConstantwrapMapToPropsFunc的实现,因为最终的initMapStateToPropsinitDispatchToProps都是由这两个函数其中之一返回的

wrapMapToPropsConstant

wrapMapToPropsConstant的返回值会在两种情况下成为initXXXXXProps

  1. mapStateToProps或者mapDispatchToProps参数缺失,这种情况下mapToProps会返回{}
  2. mapDispatchProps传入了一个对象,这样的话,这个对象里的每个actionCreator都会被包裹,形如这样(dispatch) => { foo: (...args) dispatch(foo(...args)) },这种情况下mapStateToProps每次调用也会返回一个常量(除非reducer被replaceReducer整个替换掉,不过这样的话initXXXXProps也会被重新调用了)
function wrapMapToPropsConstant(getConstant) {
  return function initConstantSelector(dispatch, options) {
    // 调用`initXXXXToProps`时就会计算好这个要返回的常量
    const constant = getConstant(dispatch, options)

    // 然后mapStateToProps或mapDispatchToProps调用的时候会返回init执行时的constant的引用
    function constantSelector() { return constant }
    // 告诉Selector,当State改变时,不需要更新我,直接用原来缓存的返回值
    constantSelector.dependsOnOwnProps = false
    return constantSelector
  }
}

wrapMapToPropsFunc

他会在下面2种情况下成为initXXXXProps

  1. mapStateToProps为函数
  2. mapDispatchToProps为函数
export function wrapMapToPropsFunc(mapToProps, methodName) {
  return function initProxySelector(dispatch, { displayName }) {

    // 将mapToProps代理到proxy上
    // 当mapToProps依赖ownProps时,额外传入ownProps,否则只传入state或者dispatch
    const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
      return proxy.dependsOnOwnProps
        ? proxy.mapToProps(stateOrDispatch, ownProps)
        : proxy.mapToProps(stateOrDispatch)
    }

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

    // 第一次通过代理执行mapToProps时会执行下列代码
    proxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch, ownProps) {
      // 保证之后的`mapToProps`执行时不会执行`detectFactoryAndVerify`
      proxy.mapToProps = mapToProps

      // 当mapToProps有dependsOnOwnProps属性时返回mapToProps.dependsOnOwnProps。
      // 如果没有, 若函数参数个数为1返回false,否则返回true
      proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)

      // 得到下一个props
      let props = proxy(stateOrDispatch, ownProps)

      // 若props为柯里化函数递归调用proxy
      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
  }
}

mapStateToPropsFactories

import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps'

export function whenMapStateToPropsIsFunction(mapStateToProps) {
  // 按照上面所说,mapState类型为函数时用wrapMapToPropsFunc构造initMapStateToProps
  return (typeof mapStateToProps === 'function')
    ? wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps')
    : undefined
}
  // 否则用wrapMapToPropsConstant并传入一个空函数构造initMapStateToProps,这个initMapStateToProps会永远返回一个空的对象`{}`
export function whenMapStateToPropsIsMissing(mapStateToProps) {
  return (!mapStateToProps)
    ? wrapMapToPropsConstant(() => ({}))
    : undefined
}

export default [
  whenMapStateToPropsIsFunction,
  whenMapStateToPropsIsMissing
]

mapDispatchToPropsFactories

import { bindActionCreators } from 'redux'
import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps'

// mapDispatchToProps类型是函数时使用wrapMapToPropsFunc的返回值作为initWrapMapToFunc
export function whenMapDispatchToPropsIsFunction(mapDispatchToProps) {
  return (typeof mapDispatchToProps === 'function')
    ? wrapMapToPropsFunc(mapDispatchToProps, 'mapDispatchToProps')
    : undefined
}

// 缺失时,默认调用dispatchToProps会返回一个对象,对象里的dispatch字段即是store的dispatch方法
// 通过后面的mergeToProps,这个dispatch最终能在组件中通过this.props.dispatch获得store的dispatch方法的引用
export function whenMapDispatchToPropsIsMissing(mapDispatchToProps) {
  return (!mapDispatchToProps)
    ? wrapMapToPropsConstant(dispatch => ({ dispatch }))
    : undefined
}
// mapDispatchToProps是一个对象且每个对象的value是Action creator时
// 通过bindActionCreator将每个value变成这样的一个函数`(...args) => dispatch(fooActionCreator(...args))`
export function whenMapDispatchToPropsIsObject(mapDispatchToProps) {
  return (mapDispatchToProps && typeof mapDispatchToProps === 'object')
    ? wrapMapToPropsConstant(dispatch => bindActionCreators(mapDispatchToProps, dispatch))
    : undefined
}

export default [
  whenMapDispatchToPropsIsFunction,
  whenMapDispatchToPropsIsMissing,
  whenMapDispatchToPropsIsObject
]

mergePropsFactories

import verifyPlainObject from '../utils/verifyPlainObject'

// 默认的mergeToProps,将`ownProps`,`stateProps`,`dispatchProps`合并到一个对象中作为最终的props
export function defaultMergeProps(stateProps, dispatchProps, ownProps) {
  return { ...ownProps, ...stateProps, ...dispatchProps }
}

// 当mergeProps为function时,用wrapMergePropsFunc的返回值,作为initMergeProps
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)
      // 获得下一个mergeProps

      // 非第一次运行
      if (hasRunOnce) {
        // 当传入initMergePropsProxy的pure为false时每次调用mergeProps都会返回新的引用
        // 或者当当前props和下一个props进行比较(默认是浅比较,由areMergedPropsEqual决定)不等时
        // 将下一个props更新到当前的mergedProps
        if (!pure || !areMergedPropsEqual(nextMergedProps, mergedProps))
          mergedProps = nextMergedProps

      } else {
        hasRunOnce = true
        // 第一次运行时,mergedProps为undefined
        mergedProps = nextMergedProps

        if (process.env.NODE_ENV !== 'production')
          verifyPlainObject(mergedProps, displayName, 'mergeProps')
      }
      // 返回mergedProps,如果mergedProps没有改变,那么会返回之前的mergedProps的引用
      // 这样当areMergedPropsEqual变为严格相等 (即用 === 比较)时能正确地工作
      return mergedProps
    }
  }
}

// mergeProps为函数时返回的initMergeProps
export function whenMergePropsIsFunction(mergeProps) {
  return (typeof mergeProps === 'function')
    ? wrapMergePropsFunc(mergeProps)
    : undefined
}

// mergeProps缺失时返回默认的initMergeProps
export function whenMergePropsIsOmitted(mergeProps) {
  return (!mergeProps)
    ? () => defaultMergeProps
    : undefined
}

export default [
  whenMergePropsIsFunction,
  whenMergePropsIsOmitted
]

finalPropsSelectorFactory

通过上面的mapStateToPropsFactoriesmapDispatchToPropsFactoriesmergePropsFactories, 我们得到了initMapStateToPropsinitMapDispatchToProps, initMergeProps三个工厂函数。 这三个工厂函数最终会在finalPropsSelectorFactory中被调用

function finalPropsSelectorFactory(dispatch, {
  initMapStateToProps,
  initMapDispatchToProps,
  initMergeProps,
  ...options
}) {
  // 调用三个工厂函数,得到`mapStateToProps`,`mapDispatchToProps`,`mergeProps`
  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)
  }

  // 当createConnect的pure选项为true时,
  // React redux会缓存`mapStateToProps`,`mapDispatchToProps`,`mergeProps`的结果
  // 减少不必要的re-render
  // 这样做有利于性能的提高
  const selectorFactory = options.pure
    ? pureFinalPropsSelectorFactory
    : impureFinalPropsSelectorFactory

  return selectorFactory(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps,
    dispatch,
    options
  )
}

impureFinalPropsSelectorFactory

function impureFinalPropsSelectorFactory(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps,
  dispatch
) {
  return function impureFinalPropsSelector(state, ownProps) {
    // 当pure为false时,不对`mapStateToProps`,`mapDispatchToProps`, `mergeProps`结果做缓存
    // 直接返回最终传给被connect包裹组件的props
    return mergeProps(
      mapStateToProps(state, ownProps),
      mapDispatchToProps(dispatch, ownProps),
      ownProps
    )
  }
}

pureFinalPropsSelectorFactory

在这段代码下面,作者留了这样一段注释

// If pure is true, the selector returned by selectorFactory will memoize its results,
// allowing connectAdvanced's shouldComponentUpdate to return false if final
// props have not changed. If false, the selector will always return a new
// object and shouldComponentUpdate will always return true.

如果pure是true,那么selector将会缓存它自己的返回值,并且允许connectAdvanced 通过shouldComponentUpdate返回最终的props是否变化。 如果pure为false,shouldComponentUpdate会永远返回true。这样当store的state改变时,传给被Connect包裹的组件的props改变,组件就必定会re-render

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

  // 当第一次调用selector时会执行这里的代码
  // 计算props,存储在变量中,返回最终的props。
  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
  }

  // state和ownProps都改变的时候
  function handleNewPropsAndNewState() {
    // 从state和ownProps得到新的stateProps
    stateProps = mapStateToProps(state, ownProps)

    // 仅当mapDispatchToProps具有第二个参数ownProps,才更新dispatchProps
    if (mapDispatchToProps.dependsOnOwnProps)
      dispatchProps = mapDispatchToProps(dispatch, ownProps)

    // 这里的更新mergedProps是必要的。因为ownProps变化后组件需要rerender。
    // Connect组件中,会使用strictEqual来判断最终的finalProps是否相等以决定shouldComponentUpdate的值
    // 因此必须返回一个新的引用使得组件更新
    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    // 返回最终的props
    return mergedProps
  }

  function handleNewProps() {
    // 仅仅当mapStateToProps依赖ownProps时更新stateProps
    if (mapStateToProps.dependsOnOwnProps)
      stateProps = mapStateToProps(state, ownProps)

    // 仅仅当mapDispatchToProps依赖ownProps时更新dispatchProps
    if (mapDispatchToProps.dependsOnOwnProps)
      dispatchProps = mapDispatchToProps(dispatch, ownProps)

    // 这里的更新mergedProps是必要的。因为ownProps变化后组件需要rerender。
    // Connect组件中,会使用strictEqual来判断最终的finalProps是否相等以决定shouldComponentUpdate的值
    // 因此必须返回一个新的引用使得组件更新
    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    return mergedProps
  }

  // 仅仅当state改变而ownProps未改变时
  function handleNewState() {

    const nextStateProps = mapStateToProps(state, ownProps)
    const statePropsChanged = !areStatePropsEqual(nextStateProps, stateProps)
    stateProps = nextStateProps

    // 如果stateProps变化才会更新mergeProps
    if (statePropsChanged)
      mergedProps = mergeProps(stateProps, dispatchProps, ownProps)

    // 返回新的或者旧的megeProps。
    // 返回旧的mergeProps时,组件的shouldComponentUpdate会为false,就不会rerender了。
    return mergedProps
  }

  // 第一次调用selector之后再调用selector会执行这个函数
  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()
    // state和ownProps都没有变化时返回旧的mergedProps,这样被Connect包裹的组件就不会更新
    return mergedProps
  }

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

Provider组件

这个组件包裹在所有需要读取redux store组件的最外围。目的是通过React legacy context,使得被Provider包裹的组件可以通过context取得store的state和subscription。

import { Component, Children } from 'react'
import PropTypes from 'prop-types'
import { storeShape, subscriptionShape } from '../utils/PropTypes'
import warning from '../utils/warning'

let didWarnAboutReceivingStore = false
function warnAboutReceivingStore() {
  if (didWarnAboutReceivingStore) {
    return
  }
  didWarnAboutReceivingStore = true

  warning(
    '<Provider> does not support changing `store` on the fly. ' +
    'It is most likely that you see this error because you updated to ' +
    'Redux 2.x and React Redux 2.x which no longer hot reload reducers ' +
    'automatically. See https://github.com/reduxjs/react-redux/releases/' +
    'tag/v2.0.0 for the migration instructions.'
  )
}

export function createProvider(storeKey = 'store') {
    const subscriptionKey = `${storeKey}Subscription`

    class Provider extends Component {
      // 子组件可以通过指定context type来取得store和subscription
        getChildContext() {
          return { [storeKey]: this[storeKey], [subscriptionKey]: null }
        }

      // 因为用法是`<Provider store={store}><Foo /></Provider>`
      // 所以将props的store引用绑定到this上
        constructor(props, context) {
          super(props, context)
          this[storeKey] = props.store;
        }

        render() {
          // 断言组件Children是单个组件
          return Children.only(this.props.children)
        }
    }

    if (process.env.NODE_ENV !== 'production') {
      // 和旧版本兼容需要做的判断
      Provider.prototype.componentWillReceiveProps = function (nextProps) {
        if (this[storeKey] !== nextProps.store) {
          warnAboutReceivingStore()
        }
      }
    }

    // 自己的PropTypes
    Provider.propTypes = {
        store: storeShape.isRequired,
        children: PropTypes.element.isRequired,
    }

    // Children的contextTypes,只有`childContextTypes`和`getChildContext`在一个组件中同时存在
    // React才会将这些信息给所有的子组件
    Provider.childContextTypes = {
        [storeKey]: storeShape.isRequired,
        [subscriptionKey]: subscriptionShape,
    }

    return Provider
}

export default createProvider()

Subscription

这部分在Redux自身的listener的基础上又包了一层自己的listener 保证祖先会比后代组件先渲染 Subscription上的订阅通过addNestedSubscribe, 最外层Connect组件上添加自身的this.onStateChange, 其后的子孙Connect组件上Subscription调用trySubscribe时,它们的onStateChange都会被 添加到他们的父listener上。这样的话。当store的state改变时,会先调用最外层的onStateChange。 之后,从最外层的Subscription开始,依次调用它们子组件的onStateChange,保证当state改变后组件是从最外层 开始更新的

// encapsulates the subscription logic for connecting a component to the redux store, as
// well as nesting subscriptions of descendant components, so that we can ensure the
// ancestor components re-render before descendants

const CLEARED = null
const nullListeners = { notify() { } }
// 组件被卸载之后组件仍然调用`notifyNestedSubs`时的情况

function createListenerCollection() {
  // the current/next pattern is copied from redux's createStore code.
  // TODO: refactor+expose that code to be reusable here?
  let current = []
  let next = []

  return {
    clear() {
      next = CLEARED
      current = CLEARED
    },

    notify() {
      // 这部分逻辑是和redux中一样的。依次调用每个listener
      // 因为Redux限制了在notify过程中listener改变时,对Listener的改变直到下一次notify才会生效
      const listeners = current = next
      for (let i = 0; i < listeners.length; i++) {
        listeners[i]()
      }
    },

    get() {
      return next
    },

    subscribe(listener) {
      let isSubscribed = true
      if (next === current) next = current.slice()
      next.push(listener)
      
      // subscribe返回unsubscribe函数,逻辑和redux一样
      return function unsubscribe() {
        if (!isSubscribed || current === CLEARED) return
        isSubscribed = false

        if (next === current) next = current.slice()
        next.splice(next.indexOf(listener), 1)
      }
    }
  }
}

export default class Subscription {
  constructor(store, parentSub, onStateChange) {
    this.store = store
    this.parentSub = parentSub
    this.onStateChange = onStateChange

    // 当Subcription订阅后,Listener的unsubscribe函数会被绑定到这个变量上
    this.unsubscribe = null
    this.listeners = nullListeners
  }

  // 后代的listener会最终订阅到他的父Subscription上
  // 当store中的state更新后,会先调用父组件的onStateChange再调用子组件的onStateChange
  addNestedSub(listener) {
    this.trySubscribe()
    return this.listeners.subscribe(listener)
  }

  notifyNestedSubs() {
    this.listeners.notify()
  }

  isSubscribed() {
    return Boolean(this.unsubscribe)
  }

  trySubscribe() {
    // 当前subscribe没有订阅时
    if (!this.unsubscribe) {
      // 将取消这个订阅的函数绑定到this.unsubscribe上
      this.unsubscribe = this.parentSub
        ? this.parentSub.addNestedSub(this.onStateChange)
        : this.store.subscribe(this.onStateChange)
      // 如果有父subscribe,则将onStateChange订阅到父SubScription上
      this.listeners = createListenerCollection()
    }
  }

  tryUnsubscribe() {
    // 取消订阅
    if (this.unsubscribe) {
      this.unsubscribe()
      this.unsubscribe = null
      this.listeners.clear()
      this.listeners = nullListeners
    }
  }
}

connect

通过connect函数,会返回一个高阶组件,调用这个高阶组件会返回Connect组件

function noop() {}

// 这个函数的作用是每次调用selector的时候保存当前当前的props,并与上次的props做strict equal
// 当相等时,将shouldComponentUpdate设置为比较的结果。
// Connect组件的shouldComponentUpdate返回了了selector的shouldComponentUpdate
// 这样可以减少不必要的rerender
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
}

export default function connectAdvanced(
  /*
    selectorFactory is a func that is responsible for returning the selector function used to
    compute new props from state, props, and dispatch. For example:

      export default connectAdvanced((dispatch, options) => (state, props) => ({
        thing: state.things[props.thingId],
        saveThing: fields => dispatch(actionCreators.saveThing(props.thingId, fields)),
      }))(YourComponent)

    Access to dispatch is provided to the factory so selectorFactories can bind actionCreators
    outside of their selector as an optimization. Options passed to connectAdvanced are passed to
    the selectorFactory, along with displayName and WrappedComponent, as the second argument.

    Note that selectorFactory is responsible for all caching/memoization of inbound and outbound
    props. Do not use connectAdvanced directly without memoizing results between calls to your
    selector, otherwise the Connect component will re-render on every state or props change.
  */
  selectorFactory,
  // options object:
  {
    // the func used to compute this HOC's displayName from the wrapped component's displayName.
    // probably overridden by wrapper functions such as connect()
    getDisplayName = name => `ConnectAdvanced(${name})`,

    // shown in error messages
    // probably overridden by wrapper functions such as connect()
    methodName = 'connectAdvanced',

    // if defined, the name of the property passed to the wrapped element indicating the number of
    // calls to render. useful for watching in react devtools for unnecessary re-renders.
    renderCountProp = undefined,

    // determines whether this HOC subscribes to store changes
    shouldHandleStateChanges = true,

    // the key of props/context to get the store
    storeKey = 'store',

    // if true, the wrapped element is exposed by this HOC via the getWrappedInstance() function.
    withRef = false,

    // additional options are passed through to the selectorFactory
    ...connectOptions
  } = {}
) {
  const subscriptionKey = storeKey + 'Subscription'
  const version = hotReloadingVersion++

  const contextTypes = {
    [storeKey]: storeShape,
    [subscriptionKey]: subscriptionShape,
  } // 接收Prodiver的contextTypes


  // Connect提供了
  const childContextTypes = {
    [subscriptionKey]: subscriptionShape,
  }


  // 返回一高阶组件`wrapWithConnect`
  return function wrapWithConnect(WrappedComponent) {
    invariant(
      typeof WrappedComponent == 'function',
      `You must pass a component to the function returned by ` +
      `${methodName}. Instead received ${JSON.stringify(WrappedComponent)}`
    )

    const wrappedComponentName = WrappedComponent.displayName
      || WrappedComponent.name
      || 'Component'

    // 组件的dispayName,安装React dev tool后可以在Chrome的调试工具中看到
    const displayName = getDisplayName(wrappedComponentName)

    const selectorFactoryOptions = {
      ...connectOptions,
      getDisplayName,
      methodName,
      renderCountProp,
      shouldHandleStateChanges,
      storeKey,
      withRef,
      displayName,
      wrappedComponentName,
      WrappedComponent
    }

    // TODO Actually fix our use of componentWillReceiveProps
    /* eslint-disable react/no-deprecated */

    class Connect extends Component {
      constructor(props, context) {
        super(props, context)

        this.version = version
        this.state = {}
        this.renderCount = 0
        // 为何从props获得store引用,原因不明
        this.store = props[storeKey] || context[storeKey]
        this.propsMode = Boolean(props[storeKey])
        this.setWrappedInstance = this.setWrappedInstance.bind(this)

        invariant(this.store,
          `Could not find "${storeKey}" in either the context or props of ` +
          `"${displayName}". Either wrap the root component in a <Provider>, ` +
          `or explicitly pass "${storeKey}" as a prop to "${displayName}".`
        )


        // 初始化selector
        this.initSelector()
        this.initSubscription()
      }

      getChildContext() {
        // If this component received store from props, its subscription should be transparent
        // to any descendants receiving store+subscription from context; it passes along
        // subscription passed to it. Otherwise, it shadows the parent subscription, which allows
        // Connect to control ordering of notifications to flow top-down.
        const subscription = this.propsMode ? null : this.subscription
        return { [subscriptionKey]: subscription || this.context[subscriptionKey] }
      }

      componentDidMount() {
        if (!shouldHandleStateChanges) return

        // componentWillMount fires during server side rendering, but componentDidMount and
        // componentWillUnmount do not. Because of this, trySubscribe happens during ...didMount.
        // Otherwise, unsubscription would never take place during SSR, causing a memory leak.
        // To handle the case where a child component may have triggered a state change by
        // dispatching an action in its componentWillMount, we have to re-run the select and maybe
        // re-render.
        // Store的订阅在服务端渲染中是不需要的。DidMount只会在浏览器上渲染时执行
        this.subscription.trySubscribe()
        // 有时候可能在componentWillMount中dispatch Action导致State被改变
        // 所以需要重新run一遍slector来获得新的props并决定是否需要更新组件
        this.selector.run(this.props)
        if (this.selector.shouldComponentUpdate) this.forceUpdate()
      }

      // 当下一个ownProps改变的时候,重新执行selctor生成新的props
      componentWillReceiveProps(nextProps) {
        this.selector.run(nextProps)
      }

      // 如上面所说,每次执行selector.run都会比较这次和上次selector返回的结果
      // 并把结果保存在selector.shouldComponentUpdate中
      // Connect的shouldComponentUpdate通过返回selector.shouldComponentUpdate来决定组件是否更新
      shouldComponentUpdate() {
        return this.selector.shouldComponentUpdate
      }

      // 组件卸载的时候做的一些善后工作
      componentWillUnmount() {
        if (this.subscription) this.subscription.tryUnsubscribe()
        this.subscription = null
        this.notifyNestedSubs = noop
        this.store = null
        this.selector.run = noop
        this.selector.shouldComponentUpdate = false
      }

      getWrappedInstance() {
        invariant(withRef,
          `To access the wrapped instance, you need to specify ` +
          `{ withRef: true } in the options argument of the ${methodName}() call.`
        )
        return this.wrappedInstance
      }

      // 通过ref到Connect上再调用this.ref.xxx.setWrappedInstance能获得这个被包裹组件的引用
      // React 16.3的forwardRef可以替代这个问题,让connect的ref指向被包裹的组件
      setWrappedInstance(ref) {
        this.wrappedInstance = ref
      }

      // 初始化selector
      // selector的作用有
      // 1. 调用selector.run,当组件更新时,获得下一个props
      // 2. 通过selector.shouldComponentUpdate判断组件是否需要更新
      initSelector() {
        const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions)
        this.selector = makeSelectorStateful(sourceSelector, this.store)
        this.selector.run(this.props)
      }

      initSubscription() {
        if (!shouldHandleStateChanges) return

        // parentSub's source should match where store came from: props vs. context. A component
        // connected to the store via props shouldn't use subscription from context, or vice versa.
        // 组件通过props来访问到store的时候是不应该访问到subscription的。
        const parentSub = (this.propsMode ? this.props : this.context)[subscriptionKey]
        this.subscription = new Subscription(this.store, parentSub, this.onStateChange.bind(this))

        // `notifyNestedSubs` is duplicated to handle the case where the component is  unmounted in
        // the middle of the notification loop, where `this.subscription` will then be null. An
        // extra null check every change can be avoided by copying the method onto `this` and then
        // replacing it with a no-op on unmount. This can probably be avoided if Subscription's
        // listeners logic is changed to not call listeners that have been unsubscribed in the
        // middle of the notification loop.
        // 当发布订阅消息的过程中可能组件被卸载,那样的话this.subscription为null。
        // ,将this.subscription bind到notifyNestedSubs上可以避免每次执行notifyNestedSubs判断this是否为null
        // 当组件被卸载后,subscription会执行空函数。
        this.notifyNestedSubs = this.subscription.notifyNestedSubs.bind(this.subscription)
      }

      // 当store的state改变时会调用这个函数
      onStateChange() {
        // 重新用selector获取props
        this.selector.run(this.props)

        // 组件不更新的情况 
        if (!this.selector.shouldComponentUpdate) {
          this.notifyNestedSubs()
          //执行listener
        } else {

          this.componentDidUpdate = this.notifyNestedSubsOnComponentDidUpdate
          // 通过setState来触发组件更新
          this.setState(dummyState)
        }
      }

      notifyNestedSubsOnComponentDidUpdate() {
        // `componentDidUpdate` is conditionally implemented when `onStateChange` determines it
        // needs to notify nested subs. Once called, it unimplements itself until further state
        // changes occur. Doing it this way vs having a permanent `componentDidUpdate` that does
        // a boolean check every time avoids an extra method call most of the time, resulting
        // in some perf boost.
        // 比起每次都在componentDidUpdate里判断是否是onStateChange导致的更新,这种方法更高效
        // 只有由于onStateChange导致的更新才会去通知listener。其它情况下不会执行他
        // 还有这种操作.jpg
        this.componentDidUpdate = undefined
        this.notifyNestedSubs()
      }

      isSubscribed() {
        return Boolean(this.subscription) && this.subscription.isSubscribed()
      }

      addExtraProps(props) {
        if (!withRef && !renderCountProp && !(this.propsMode && this.subscription)) return props
        // make a shallow copy so that fields added don't leak to the original selector.
        // this is especially important for 'ref' since that's a reference back to the component
        // instance. a singleton memoized selector would then be holding a reference to the
        // instance, preventing the instance from being garbage collected, and that would be bad
        // 做一个浅拷贝,让外部通过ref引用selector时,不会出现selector缺失的情况(组件被卸载,然后Connect
        // 的selector引用被设置为null)
        // 一个缓存selector的单例会保持对组件实例的引用,这样组件实例会无法被垃圾回收
        // emmm让我想想这怎么实现
        const withExtras = { ...props }
        if (withRef) withExtras.ref = this.setWrappedInstance
        if (renderCountProp) withExtras[renderCountProp] = this.renderCount++
        if (this.propsMode && this.subscription) withExtras[subscriptionKey] = this.subscription
        return withExtras
      }

      render() {
        const selector = this.selector
        selector.shouldComponentUpdate = false

        if (selector.error) {
          throw selector.error
        } else {
          return createElement(WrappedComponent, this.addExtraProps(selector.props))
        }
      }
    }

    /* eslint-enable react/no-deprecated */
    // 将一些东西绑定到Connect的static filed上
    // 比如contextTypes
    Connect.WrappedComponent = WrappedComponent
    Connect.displayName = displayName
    Connect.childContextTypes = childContextTypes
    Connect.contextTypes = contextTypes
    Connect.propTypes = contextTypes

    // 热重载的情况下重新初始化listener
    if (process.env.NODE_ENV !== 'production') {
      Connect.prototype.componentWillUpdate = function componentWillUpdate() {
        // We are hot reloading!
        if (this.version !== version) {
          this.version = version
          this.initSelector()

          // If any connected descendants don't hot reload (and resubscribe in the process), their
          // listeners will be lost when we unsubscribe. Unfortunately, by copying over all
          // listeners, this does mean that the old versions of connected descendants will still be
          // notified of state changes; however, their onStateChange function is a no-op so this
          // isn't a huge deal.
          let oldListeners = [];

          if (this.subscription) {
            oldListeners = this.subscription.listeners.get()
            this.subscription.tryUnsubscribe()
          }
          this.initSubscription()
          if (shouldHandleStateChanges) {
            this.subscription.trySubscribe()
            oldListeners.forEach(listener => this.subscription.listeners.subscribe(listener))
          }
        }
      }
    }

    // 这个hoistStatic会将WrappedComponent中非React的staticFileds绑定到Connect组件上
    return hoistStatics(Connect, WrappedComponent)
  }
}

后记

只看懂了90%。还有三个地方不理解

  1. 为何能从props中能获取到store
  2. addExtraProps里的骚操作
  3. Connect为何要重新声明childContextTypes和getChildContext

参考资料

React-redux github repo

React-redux API reference

Redux API refenrence