热门搜索 :
考研考公
您的当前位置:首页正文

react-redux connect 源码

来源:东饰资讯网

今天看了下react-redux的源码,主要来看下connect的方法

首先找到connect的入口文件。在src/index.js下找到。对应connect文件夹下的connect.js文件。

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

我们知道,在容器组件平时对外暴露的时候是

const mapStateToProps = (state, ownProps) => ({
//  ...
})

const mapDispatchToProps = dispatch => ({
  actions: bindActionCreators(Object.assign({}, actions), dispatch)
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(YourComponent)

我们可以看到基本都是都是传递两个参数,mapStateToProps&mapDispatchToProps, 第三个参数mergeProps并没有传递,这个参数是干什么的?
他在

const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')
  1. whenMergePropsIsFunction存在mergeProps并且是function的时候,,这个函数的第一个参数是一个dispatch,第二个参数是一个对象,并且对象接收三个属性,如果不是一个方法,那就返回undefined.

mapStateToProps&mapDispatchToPropsmergeProps的初始化类似,都会进行判断再去操作。

然后connect后面的第四个参数是一个对象,他接收一些属性。

然后就是下一步了,在使用的时候,我们connect()之后就是继续调用,传参是我们的容器组件。那这时,在源码里就是对应的return connectHOC(selectorFactory, {...}), 这个返回的是一个函数运行的结果,所以我们传参的容器组件这个参数,并不是这里的selectorFactory,而是他运行完成之后的结果所接受的参数。

在这个方法里,有返回一个方法wrapWithConnect(WrappedComponent){},好家伙,到这里我们可以知道connect里的return createHOC(...)的结果返回的就是这个,然后再看返回的这个函数名称,很见名知意用connect包裹的函数的参数是包裹的组件.那就看看这个函数里面是啥。

// 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.

就是去处理到底是否应该重新渲染,所有如果你想要刷新的情况,可以在connect的传参的第四个对象里改变purefalse.

然后initSelector同时也会为当前对象创建一个selector

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
}

最后来run,如果符合条件或者出错,便会改变shouldComponentUpdate = true,去重新 render.

再来看看initSubscription方法:

initSubscription() {
        if (!shouldHandleStateChanges) return

        const parentSub = (this.propsMode ? this.props : this.context)[subscriptionKey]
        this.subscription = new Subscription(this.store, parentSub, this.onStateChange.bind(this))

        this.notifyNestedSubs = this.subscription.notifyNestedSubs.bind(this.subscription)
      }

先判断shouldHandleStateChanges是不是成立,如果成立则进行,否则返回。成立会进行初始化,正如其名,初始化订阅相关事件。

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.run(this.props)
        if (this.selector.shouldComponentUpdate) this.forceUpdate()
}

最后看下render方法,

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

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

可以看到.他会对属性进行重写成false,如果报错,就跑出错误,如果正常,那就进行render创建元素到页面。

Top