一起学react(4) 史上最详细react-redux 源码分析

今天来分析一下react-redux源码
react-redux所有代码注释地址:https://github.com/fangkyi03/react-redux.git
如果大家有问题的话 可以加我QQ:469373256
写完这个文章我也是心力交瘁了 react-redux 一些数据都是放在闭包里面的 然后循环的调用有点蒙圈的感觉

  • 接口预览
    react-redux只暴露了这几个接口 接下来 将会按照代码的执行顺序 进行一一的分析
import Provider, { createProvider } from './components/Provider'
import connectAdvanced from './components/connectAdvanced'
import connect from './connect/connect'

export { Provider, createProvider, connectAdvanced, connect }
  • Provider
    先来看一下函数定义部分
export function createProvider(storeKey = 'store', subKey) 
export default createProvider()
import Provider, { createProvider } from './components/Provider'

我们默认如果直接使用react-redux中的Provider的话 默认传递过去的数据是不可控制的 如果你想做一些自定义设置的话 建议选择引入createProvider

  • Provider 主体部分
export function createProvider(storeKey = 'store', subKey) {
    如果subKey有值就使用传入的值 否则使用store
     所以最后的结果就是变成storeSubscription
    const subscriptionKey = subKey || `${storeKey}Subscription`

    class Provider extends Component {
        react-redux使用context来实现父级对所有的子级传递数据 但是这个context已经在16.3版本有了新的方法 这里就不多讲了
          
        首先 如果你要让子获取到父的数据 那么肯定得有一个入口 这个getChildContext就是子固定的入口 它会去判断自身的ContextTypes是否有数据 如果有的话 就会直接调用父级的这个getChildContext来返回对应的数据
        getChildContext() {
          return { [storeKey]: this[storeKey], [subscriptionKey]: null }
        }

        constructor(props, context) {
          super(props, context)
          this[storeKey] = props.store;
        }

        render() {
          只允许渲染一个
          return Children.only(this.props.children)
        }
    }
    如果不是线上模式的话并且如果这个组件的父发生改变的话 将会出现错误提醒
    if (process.env.NODE_ENV !== 'production') {
      Provider.prototype.componentWillReceiveProps = function (nextProps) {
        if (this[storeKey] !== nextProps.store) {
          warnAboutReceivingStore()
        }
      }
    }
    
   对于props组件类型的定义
    Provider.propTypes = {
        store: storeShape.isRequired,
        children: PropTypes.element.isRequired,
    }

  这里是要传递给子组件的数据类型定义
    Provider.childContextTypes = {
        [storeKey]: storeShape.isRequired,
        [subscriptionKey]: subscriptionShape,
    }

    return Provider
}

ok 这个组件就分析到此 没有什么可以太多讲的 需要注意的就是context 但是一般我们不会接触到这个

  • connect核心完整代码
import connectAdvanced from '../components/connectAdvanced'
import shallowEqual from '../utils/shallowEqual'
import defaultMapDispatchToPropsFactories from './mapDispatchToProps'
import defaultMapStateToPropsFactories from './mapStateToProps'
import defaultMergePropsFactories from './mergeProps'
import defaultSelectorFactory from './selectorFactory'

/*
  connect is a facade over connectAdvanced. It turns its args into a compatible
  selectorFactory, which has the signature:

    (dispatch, options) => (nextState, nextOwnProps) => nextFinalProps
  
  connect passes its args to connectAdvanced as options, which will in turn pass them to
  selectorFactory each time a Connect component instance is instantiated or hot reloaded.

  selectorFactory returns a final props selector from its mapStateToProps,
  mapStateToPropsFactories, mapDispatchToProps, mapDispatchToPropsFactories, mergeProps,
  mergePropsFactories, and pure args.

  The resulting final props selector is called by the Connect component instance whenever
  it receives new props or store state.
 */

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

function strictEqual(a, b) { return a === b }

// createConnect with default args builds the 'official' connect behavior. Calling it with
// different options opens up some testing and extensibility scenarios
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, {
      // used in error messages
      methodName: 'connect',

       // used to compute Connect's displayName from the wrapped component's displayName.
      getDisplayName: name => `Connect(${name})`,

      // if mapStateToProps is falsy, the Connect component doesn't subscribe to store state changes
      shouldHandleStateChanges: Boolean(mapStateToProps),

      // passed through to selectorFactory
      initMapStateToProps,
      initMapDispatchToProps,
      initMergeProps,
      pure,
      areStatesEqual,
      areOwnPropsEqual,
      areStatePropsEqual,
      areMergedPropsEqual,

      // any extra options args can override defaults of connect or connectAdvanced
      ...extraOptions
    })
  }
}

export default createConnect()

  • createConnect部分
export function createConnect({
  connectHOC = connectAdvanced,
  mapStateToPropsFactories = defaultMapStateToPropsFactories,
  mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
  mergePropsFactories = defaultMergePropsFactories,
  selectorFactory = defaultSelectorFactory
} = {}) 
export default createConnect()
import connect from './connect/connect'

先来看一下函数定义 从上面的函数定义可以看到 其实是有暴露createConnect来进行使用的 只不过默认使用的是不传参的createConnect导致全部都走了默认值 但是其实 如果你想做一些自定义设置的话 可以直接调用createConnect来实现

  • connect部分
  return function connect(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps,
    {
      pure = true,
      areStatesEqual = strictEqual,
      areOwnPropsEqual = shallowEqual,
      areStatePropsEqual = shallowEqual,
      areMergedPropsEqual = shallowEqual,
      ...extraOptions
    } = {}
  ) 
这里其实就是我们平常在用的connect了 第四个参数一般用来传入pure或者withRef来控制是否使用pure跟是否返回ref withRef等其他的参数都存在了extraOptions这个里面
  • initProps初始化部分
    const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps')
    const initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps')
    const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')

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 为对应的判断条件类型是一个数组 只要你的判断条件中 有任意一条符合条件 就直接抛出 否则返回 一个错误
> * initMapStateToProps部分
这个根据mapStateToProps是否为undefined分为两部分
whenMapStateToPropsIsFunction 
whenMapStateToPropsIsMissing

initMapStateToProps

  • wrapMapToPropsConstant 部分详解
export function wrapMapToPropsConstant(getConstant) {
  return function initConstantSelector(dispatch, options) {
    const constant = getConstant(dispatch, options)
    function constantSelector() { return constant }
    constantSelector.dependsOnOwnProps = false 
    return constantSelector
  }
}

wrapMapToPropsConstant(() => ({}))
从这个例子里面看 这里写这么多代码其实是无意义的 但是 这个主要是为了留给后面使用
wrapMapToPropsConstant(dispatch => bindActionCreators(mapDispatchToProps, dispatch))
  • wrapMapToPropsFunc 部分详解
export function getDependsOnOwnProps(mapToProps) {
  return (mapToProps.dependsOnOwnProps !== null && mapToProps.dependsOnOwnProps !== undefined)
    ? Boolean(mapToProps.dependsOnOwnProps)
    : mapToProps.length !== 1
}

// Used by whenMapStateToPropsIsFunction and whenMapDispatchToPropsIsFunction,
// this function wraps mapToProps in a proxy function which does several things:
// 
//  * Detects whether the mapToProps function being called depends on props, which
//    is used by selectorFactory to decide if it should reinvoke on props changes.
//    
//  * On first call, handles mapToProps if returns another function, and treats that
//    new function as the true mapToProps for subsequent calls.
//    
//  * On first call, verifies the first result is a plain object, in order to warn
//    the developer that their mapToProps function is not returning a valid result.
//    
export function wrapMapToPropsFunc(mapToProps, methodName) {
  return function initProxySelector(dispatch, { displayName }) {
    // 当程序执行到这里的时候 其实proxy并没有被运行 只是返回了一个proxy的声明
    // 会在handleFirstCall被进行调用来初始化
    // 因为刚才程序已经走了一遍的关系 这里dependsOnOwnProps默认会变成true
    // 所以会走下面的那个proxy.mapToProps
    const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
      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进行复制 这边要进行运算了 只要mapToProps返回的内容不为function 并且等于object的话 程序就直接结束了
      // 否则就会继续赋值不断的遍历执行 知道返回了object以后 才将最终的结果返回到最外层
      proxy.mapToProps = mapToProps
      proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)
      // 这里赋值以后 又会重新的走上面那个函数 但是因为现在用的是我们自己写的mapStateToProps函数了 所以不可能会有dependsOnOwnProps
      // 所以这边的dependsOnOwnProps永远会是false 因为false的关系 所以会直接调用mapStateToProps(stateOrDispatch)
      let props = proxy(stateOrDispatch, ownProps)

      if (typeof props === 'function') {
        // 只要返回的不是function并且等于object的话 这边就结束了 直接返回新的数据 否则不停的深层执行
        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
  }
}
之前我这一部分也没有完全看懂 这里的设计非常巧妙 正常情况下 我们的mapStateToProps返回的就是一个普通的对象 但是mapStateToProps其实支持返回一个function 那么问题就来了 因为每个人的书写习惯不一致的关系 所以我在mapStateToProps返回了一个闭包以后 要如何正常获取数据呢 这句代码的用处就是不断的遍历直到你返回的数据为一个object才放行
  • initMapDispatchToProps部分

这里分成了三个部分 前面两个部分 跟上面讲的是一样的就不多说了 主要说一下第三个部分
whenMapDispatchToPropsIsFunction = whenMapStateToPropsIsFunction
whenMapDispatchToPropsIsMissing = whenMapStateToPropsIsMissing
whenMapDispatchToPropsIsObject

  • whenMapDispatchToPropsIsObject 部分详解
export function whenMapDispatchToPropsIsObject(mapDispatchToProps) {
  return (mapDispatchToProps && typeof mapDispatchToProps === 'object')
    ? wrapMapToPropsConstant(dispatch => bindActionCreators(mapDispatchToProps, dispatch))
    : undefined
}


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

    function constantSelector() { return constant }
    constantSelector.dependsOnOwnProps = false 
    return constantSelector
  }
}

说明:
假设现在的mapDispatchToProps是这样的一个结构

{
x:function(data){
return {type:'xxx',data}
  }
}
经过bindActionCreators以后就变成了
x:function(action){
  dispatch(x(action))
}
在每个function的前面都外包了一层 使得可以做到自动发起action的功能

  const keys = Object.keys(actionCreators)
  const boundActionCreators = {}
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i]
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators

function bindActionCreator(actionCreator, dispatch) {
  return (...args) => dispatch(actionCreator(...args))
}

  • initMergeProps
  • whenMergePropsIsOmitted 如果为undefined的时候 载入
export function whenMergePropsIsOmitted(mergeProps) {
  return (!mergeProps)
    ? () => defaultMergeProps
    : undefined
}

export function defaultMergeProps(stateProps, dispatchProps, ownProps) {
  return { ...ownProps, ...stateProps, ...dispatchProps }
}
这里不多说 就是合并数据用的 
  • whenMergePropsIsFunction 如果mergeProps为function的时候
export function whenMergePropsIsFunction(mergeProps) {
  return (typeof mergeProps === 'function')
    ? wrapMergePropsFunc(mergeProps)
    : undefined
}

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)

      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
    }
  }
}
这部分后续补上 因为用的不是特别的多
  • connectHOC 部分
    return connectHOC(selectorFactory, {
      // used in error messages
      methodName: 'connect',

       // used to compute Connect's displayName from the wrapped component's displayName.
      getDisplayName: name => `Connect(${name})`,

      // if mapStateToProps is falsy, the Connect component doesn't subscribe to store state changes
      shouldHandleStateChanges: Boolean(mapStateToProps),

      // passed through to selectorFactory
      initMapStateToProps,
      initMapDispatchToProps,
      initMergeProps,
      pure,
      areStatesEqual,
      areOwnPropsEqual,
      areStatePropsEqual,
      areMergedPropsEqual,

      // any extra options args can override defaults of connect or connectAdvanced
      ...extraOptions
    })

connectHOC中return了这个函数 这就是为什么我们的connect是
connect()()这样调用的原因 这个一个高阶组件 由function返回一个view
return function wrapWithConnect(WrappedComponent) 

先看一个参数部分
    const selectorFactoryOptions = {
      ...connectOptions,
      getDisplayName,
      methodName,
      renderCountProp,
      shouldHandleStateChanges,
      storeKey,
      withRef,
      displayName,
      wrappedComponentName,
      WrappedComponent
    }

this.initSelector()
this.initSubscription()
这两行代码 决定了整个的react-redux的运行
  • initSelector
      initSelector() {
        const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions)
        this.selector = makeSelectorStateful(sourceSelector, this.store)
        this.selector.run(this.props)
      }
selectorFactory这里的这个参数是由外部的createConnect时传递进来的 看一下这个定义
  • selectorFactory
export default function finalPropsSelectorFactory(dispatch, {
  initMapStateToProps,
  initMapDispatchToProps,
  initMergeProps,
  ...options
}) {
  // 这里就会初始化wrapMapToPropsFunc中第一层的代码 并且将里面的内容返回继续往下传递 具体可以看前面的说明
  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)
  }
  // 这里就比较关键了 如果你设置了pure以后 这边走的其实是两个完全不同的路线
  // 先优先看一下比较简单的部分
  const selectorFactory = options.pure
    ? pureFinalPropsSelectorFactory
    : impureFinalPropsSelectorFactory

  return selectorFactory(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps,
    dispatch,
    options
  )
  • impureFinalPropsSelectorFactory
export function impureFinalPropsSelectorFactory(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps,
  dispatch
) {
  return function impureFinalPropsSelector(state, ownProps) {
    return mergeProps(
      mapStateToProps(state, ownProps),
      mapDispatchToProps(dispatch, ownProps),
      ownProps
    )
  }
}
这个函数的作用 就是调用mergerProps并返回最新的nextProps数据 用于进行比对 这是针对没有pure属性的 如果有pure属性 就会相对复杂一点了
  • pureFinalPropsSelectorFactory
// 这里传递进来的
// 实际上已经是运行了第一层代码的返回函数了
// mapStateToProps,
// mapDispatchToProps,
// mergeProps,
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() {
    // 执行返回state
    stateProps = mapStateToProps(state, ownProps)
    // 如果dependsOnOwnProps为true返回新的props否则使用原有的
    if (mapDispatchToProps.dependsOnOwnProps){
      dispatchProps = mapDispatchToProps(dispatch, ownProps)
    }
    // 合并所有数据
    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    return mergedProps
  }

  // 合并新的props
  function handleNewProps() {
    // 这部分可以看wrapMapToPropsFunc部分的说明 有详细解释
    // dependsOnOwnProps 正常情况 
    // 第一次执行的时候 肯定是true 第二次是个fasle 因为当时的函数里面还没有带上这个属性 
    // 第三次的话就是true了 这时候调用proxy的时候 这个属性就有了
    // 当你正常的返回了一个object以后 你就带上了这个属性
    // 那么当你下次在进行更新的时候就会直接获取新的数据了
    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) {
    // 比对props跟state是否被更改
    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
  }
  // hasRunAtLeastOnce判断是否是首次运行 如果是首次运行就跳转到handleFirstCall 
  return function pureFinalPropsSelector(nextState, nextOwnProps) {
    return hasRunAtLeastOnce
      ? handleSubsequentCalls(nextState, nextOwnProps)
      : handleFirstCall(nextState, nextOwnProps)
  }
}
  • 核心更新比对效验的部分
    areStatesEqual = strictEqual,
    areOwnPropsEqual = shallowEqual,
    areStatePropsEqual = shallowEqual,
    areMergedPropsEqual = shallowEqual,
    从这里可以看到 基本上所有的更新 都是走shallowEqual这个的 针对这个来做一下分析
export default function shallowEqual(objA, objB) {
  // 先判断两个对象是否一致 如果一致则返回true 认为不需要更新
  if (is(objA, objB)) return true
  // 只要满足null 或者 !== object 这两个条件的任意一个 都返回false 认为需要更新
  if (typeof objA !== 'object' || objA === null ||
      typeof objB !== 'object' || objB === null) {
    return false
  }

  const keysA = Object.keys(objA)
  const keysB = Object.keys(objB)
  // 如果A B 是两个对象 keys以后的长度不一致 也返回false 认为下需要更新
  if (keysA.length !== keysB.length) return false
  // 如果两者长度一致 在依次比对内部数据 看是否一致
  for (let i = 0; i < keysA.length; i++) {
    // 如果objB里面不存在keysA属性的话 直接返回fasle认为需要更新了
    // 或者如果相同数组下objA与objB不相等的话 也认为需要更新
    if (!hasOwn.call(objB, keysA[i]) ||
        !is(objA[keysA[i]], objB[keysA[i]])) {
      return false
    }
  }
  // 除此情况外 都认为不需要更新
  return true
}

更新逻辑
其中sourceSelector这个 就是上面刚讲到的那部分 每次执行返回一个最新的nextProps并且还会在跟自身原有的selector.props比对 如果不一致就将
selector.shouldComponentUpdate = true
然后在后面步骤去参与更新

constructor  =>initSelector =>makeSelectorStateful
先在构造函数中执行这个进行初始化
        this.initSelector()
        this.initSubscription()

之后在initSelector中对于数据进行初始化 这里很关键
        const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions)
        this.selector = makeSelectorStateful(sourceSelector, this.store)
        this.selector.run(this.props)

这里负责比对数据sourceSelector就是上面讲到的 会走对应闭包里面的函数 用于返回一个最新的nextProps然后跟当前的selector.props比对 如果不一样的话就返回一个最新的 如果一样就处理 也就是 一个false
selector.shouldComponentUpdate 这个会在每次render的时候 被设置为false
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
}
  • 接下来讲一下Subscription监听的部分
    先来看一下在程序中的调用顺序
        this.initSelector()
        this.initSubscription()

      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.
        // 这里有一点需要注意的就是propsMode 如果有数据的话 会从props取 没有则从context取 正常情况下
        // 简单来说 我们知道context会被继承下去 如果这里有获取到数据的话 就会直接使用原有的 而不是现有的
        // 并不是每次每个组件都使用了一个新的监听 而是从父级开始 如果父有了一个监听 就使用父的 当我这边去进行改变的时候
        // 默认其实会触发父的监听 在由父来回调当前页面的监听函数 做到尽量少的创建一个监听 实现公用
        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.
        // 这里绑定监听需要用到的函数
        // 当你第一次执行的时候 因为
        // const nullListeners = { notify() {} }
        // this.listeners = nullListeners
        // 这个时候直接执行是不会有任何效果的
        this.notifyNestedSubs = this.subscription.notifyNestedSubs.bind(this.subscription)


      }
    第一次初始化会在didMount以后被触发
      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.
        // 在这里执行第一次的监听 这里仅仅还只是加入队列中 并没有执行
        this.subscription.trySubscribe()
        // 这里计算一次返回最新的this.selector.shouldComponentUpdate
        this.selector.run(this.props)
        // 如果当前需要更新的话 采取强制更新
        if (this.selector.shouldComponentUpdate) this.forceUpdate()
      }

  trySubscribe() {
    // 当你执行了这个函数以后 
    if (!this.unsubscribe) {
      this.unsubscribe = this.parentSub
        // 
        ? this.parentSub.addNestedSub(this.onStateChange)
        // 这里使用的是redux中提供的函数 在这里当你每次dispatch的时候 就会被触发到
        : this.store.subscribe(this.onStateChange)
      // 当你走到这里的时候 listeners就被替换了 这个时候 才是真正可以使用监听了
      // 如果你不是从parent获取更新的话 默认这里使用redux来维护你的监听
      // 但是如果你有parent以后 是由内部来进行维护的
      // 内部调用的时候 其实会继续走addNestedSub
      // this.listeners.subscribe(listener)
      // 并且在这里返回一个监听
      // 这个定义的变量也只会在有父级的时候 被使用到 因为自身也有可能成为别人的父 
      // 所以这边没有做其他的判断 采取全部都定义的方式
      this.listeners = createListenerCollection()
    }
  }

ok 到这里为止 绑定就都完成了 剩下的就是等redux的store改变的时候 触发更新了 来看一下更新部分的逻辑
  • createListenerCollection部分
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() {
     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)
     // 这里模仿了redux返回了一个卸载监听的函数
     return function unsubscribe() {
       if (!isSubscribed || current === CLEARED) return
       isSubscribed = false

       if (next === current) next = current.slice()
       next.splice(next.indexOf(listener), 1)
     }
   }
 }
}
  • react-redux render部分代码
      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
        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))
        }
      }
这里比较简单就是将参数复制过去而已

react-redux所有代码注释地址:https://github.com/fangkyi03/react-redux.git
ok react-redux所有代码 到此全部玩不

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容