til
  • README
  • Software Development Roles
  • solid
  • README
    • service-worker
  • docker
    • arg
    • 更新docker版本
  • editor
    • vscode
    • Creating a VS Code Theme
  • english
    • words
  • front-end
    • ==
    • ECMAScript
    • IIFE
    • Label
    • basic
    • html.js.css渲染顺序
    • npm-vs-yarn
    • obj-delete-key-value
    • react
    • split-join-and-replace
    • video
    • 前端自检清单
    • 递归及去重
    • css
      • css换肤
      • flex
      • list
      • nth-child和nth-of-type区别
      • padding
      • position
      • 层叠上下文
      • 层叠样式(+)
      • 正方形
      • 语义化标签
    • dom
      • DOCTYPE
      • HEAD
      • 修改document
      • 自定义表单验证
    • electron
      • basic
    • es6
      • basic-type
      • basic
      • prototype-example
      • defineProperty
      • understanding-es6
        • 0.introduction
        • Appendix A: Smaller Changes
        • Appendix B: Understanding ES7
        • Block-Binding
        • Proxies&Reflection
        • class
        • 解构赋值
        • function
        • improved-array
        • iterators&generators
        • modules
        • object
        • promise
        • Map&Set
        • symbol
    • images
      • 前端角度看图片
    • interview_case
      • lexical_scope
      • redux和localstroage存储位置
    • javascript
      • fuck-the-js
      • js-engine-work
      • js原生操作dom
      • what-is-function-program
      • 执行上下文
      • articles
        • JavaScript中使用函数组合
        • JavaScript中的依赖注入
        • JavaScript作用域链中的标识符解析和闭包
        • JavaScript是何如工作的--概述
        • JavaScript深拷贝
        • JavaScript的全局变量是如何工作的
        • js继承常见的误解
        • node12&chrome中7个新的提案功能
        • 你真的懂JavaScript吗
      • date
        • index
      • engines
        • basic
        • JavaScript引擎基础:外形和内联缓存
        • v8中推测性优化的介绍
        • 优化原型
        • 更快的异步功能和promise
      • events
        • baisc
        • 事件冒泡和捕获
        • 定义事件
        • 页面生命周期
      • higher-order-function
        • curry
        • monad
      • module
        • basic
        • main&module
      • objects
        • iterator
        • spread
        • examples
          • iterator
      • performance
        • blocking-css
        • cache
      • prototype
        • Property-Descriptors
        • basic
        • prototype-shadow
      • you-dont-known-js
        • async&performance
          • Chapter 1: Asynchrony: Now & Later
          • Chapter 2: Callbacks
          • Chapter 3: Promises
          • Chapter 4: Generators
        • scope & closures
          • apA
          • apB
          • apC
          • apD
          • chapter1-what-is-scope
          • chapter2-lexical-scope
          • chapter3-function-vs-block-scope
          • chapter4-hoisting
          • chapter5-scope-closure
        • this & object prototypes
          • chapter1-this-or-that
          • chapter2-this-make-sense
          • chapter3-objects
          • chapter4-mixing(up)-class-object
          • chapter5-prototype
          • chapter6-behavior-delegation
        • types&grammer
          • Chapter1-Types
          • Chapter2-Values
          • Chapter3-Natives
          • Chapter4-coercion
          • Chapter5-grammer
        • up & going
          • chapter1-into-programming
          • chapter2-into-javascript
          • chapter3-into-YDKJS
    • mobile
      • iPhone分辨率终极指南
    • npm
      • arguments
      • build
    • react-native
      • prop-methods
    • react
      • PropTypes
      • basic
      • codebase-overview
      • component-element-instance
      • context
      • how-to-known-component-is-func-or-class
      • overview
      • react16.9
      • react18计划
      • react的设计原则
      • reconciliation
      • setState
      • useMemo
      • why-do-we-write-super-props
      • 从头实现一个react
      • concurrent
        • 引入并发模式(仅试验)
      • conf
        • conf-2019
      • events
        • 合成事件概述
      • hooks
        • custom-hook
        • effect-hook
        • hooks-api
        • intro
        • overview
        • rules
        • state-hook
        • hooks-vs-class
          • thinking-in-react-hooks
      • overreact
        • Development模式是如何工作的
        • How-Does-setState-Know-What-to-Do
        • Why-Do-React-Elements-Have-a-$$typeof-Property
        • Why-Do-React-Hooks-Rely-on-Call-Order
        • how-to-known-component-is-func-or-class
        • preparing-tach-talk-motivation
        • react作为ui运行
        • things-i-dont-known-as-2018
        • ui-element-problem-and-build-yourself
        • why-do-we-write-super-props
        • 一份完整的useEffect指南
        • 为什么X不是Hook
        • 函数组件与类有什么不同?
        • 演讲准备2-what-why-how
        • 编写弹性组件
        • 让setInterval在React-Hooks中为声明式
      • practice
        • render
      • react-dom
        • basic
      • react-redux
        • apiv7.1-hooks
        • connect
        • shallow-equal
      • redux
        • applyMiddleware
        • applyMiddleware2-细节
        • example
    • regex
      • index
    • stories
      • 数组下标
      • 阻止事件冒泡
    • svelte
      • compile-svelte-in-your-head-1
      • compiler-overview
      • parser
        • 写一个解析器-JavaScript的JSON解析器
    • turbopack
      • basic
    • typescript
      • interface和type的区别
    • webpack
      • hash
      • webpack4-for-react
      • webpack4
      • webpack4to5
      • babel
        • babel-parser和acorn的区别
        • babel.7.11
        • family
        • react16.14使用new-transform
        • update-to-7
    • pdf
      • deep-js
        • basic
      • react
        • reintroducing
  • git
    • capital
    • emoji
  • http
    • http2.0
    • response
  • rails
    • api
    • flash
    • middleware-vs-metal
    • model
    • performance
    • routes
    • environment
      • error
    • patterns
      • service
    • sidekiq
      • params
    • deploy
      • capistrano
        • ssh
  • ruby
    • self
    • net
      • http请求携带cookie
  • server
    • ss
    • ssh
    • user
    • crawler
      • puppeteer
    • nginx
      • domain-without-80
      • nginx节省带宽
  • sql
    • rails
    • search
Powered by GitBook
On this page
  1. front-end
  2. react
  3. react-redux

connect

Previousapiv7.1-hooksNextshallow-equal

Last updated 6 years ago

今天看了下react-redux的源码,主要来看下connect的方法, 当前版本5.1.0

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

  • 大致说下源码connect流程 connect.js对外暴露是通过export default createConnect(), 我们来看。 在这里,他是在对外暴露的时候直接运行,导致对外导出的结果就是他返回的一个. 然后再connect里他是返回了一个函数的结果。这个就对应到 所对应的部分,然后再返回处理过的我们的容器组件.

  • 具体细节 看下面:

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

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

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

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

export default connect( mapStateToProps, mapDispatchToProps )(YourComponent)

我们可以看到基本都是都是传递两个参数,`mapStateToProps&mapDispatchToProps`, 第三个参数`mergeProps `并没有传递,这个参数是干什么的?
他在
```js
const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')

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

他们的初始化会返回一个函数:

  • initMapStateToProps & initMapDispatchToProps

    这两个实际上是wrapMapToProps.js中的wrapMapToPropsFunc函数返回的一个initProxySelector函数.

  • initMergeProps

    如果是空mergeProps参数就使用默认的,如果mergeProps参数是一个函数,那就使用mergeProps.js的wrapMergePropsFunc并且返回initMergePropsProxy函数。

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

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

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

我们再看看这个connect class, 他在构造函数里直接运行了this.initSelector()和this.initSubscription()我们先看下initSelector:

initSelector() {
  // 这里的sourceSelector 就是`selectFactory`里的返回的闭包(pureFinalPropsSelector),
  // 在运行返回闭包之前已经处理了`initMapStateToProps`,并且那个地方返回的是`proxy`, 有标记
  // selectorFactory会在返回pureFinalPropsSelector函数(handleSubsequentCalls函数或者handleFirstCall函数)
  const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions)

  //返回selector对象,包含一个run函数
  this.selector = makeSelectorStateful(sourceSelector, this.store) 


  this.selector.run(this.props)
}
// 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的传参的第四个对象里改变pure为false.

假设这个是一个pure,那么就会继续调用pureFinalPropsSelectorFactory。看这个参数:

// 下面的这个调用是在闭包的返回基础上调用,见`connect.js`的`createConnect`方法里
// 下面的这个initMapStateToProps是相当于运行的闭包,即`wrapMapToProps`里的`initProxySelector`.
// 但是这几乎等于没有运行,因为这里直接返回了proxy函数.
const mapStateToProps = initMapStateToProps(dispatch, options) // return proxy
const mapDispatchToProps = initMapDispatchToProps(dispatch, options)
const mergeProps = initMergeProps(dispatch, options)
const selectorFactory = options.pure
    ? pureFinalPropsSelectorFactory
    : impureFinalPropsSelectorFactory

  // 上面返回了mapToProps之后,实则是返回了proxy.
  return selectorFactory(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps,
    dispatch,
    options
  )

好的,我们再看pureFinalPropsSelectorFactory函数:

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

这个函数太长,我们看他返回的。那么我们直接在看看他调用的handleFirstCall函数:

function handleFirstCall(firstState, firstOwnProps) {
    state = firstState
    ownProps = firstOwnProps
    // 这里实际上是在调用`wrapMapToProps -> wrapMapToPropsFunc`返回的`proxy`函数
    //调用proxy函数会返回props,最后把这些props给merged并返回。
    stateProps = mapStateToProps(state, ownProps)
    dispatchProps = mapDispatchToProps(dispatch, ownProps)
    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    hasRunAtLeastOnce = true
    return mergedProps
  }

好吧,这里看出来了吗?mergedProps,这又是啥,我们来看看上面说到的proxy函数:

export function wrapMapToPropsFunc(mapToProps, methodName) {
  return function initProxySelector(dispatch, { displayName }) {
    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指向`connect.js`里传递进来的`mapXxToProps`,也就是我们组建写的:
      // const mapStateToProps = (state, ownProps) => ({myNeedProps: state.yourState})
      // 不重写会死循环
      proxy.mapToProps = mapToProps
      proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)
      let props = proxy(stateOrDispatch, ownProps)

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

可以看到吧,proxy会给你返回props。

然后initSelector同时也会为当前对象创建一个selector: 我们结合上面的initSelector可以知道,sourceSelector就是selectorFactory返回的handleSubsequentCalls函数或者handleFirstCall函数:

function makeSelectorStateful(sourceSelector, store) {
  // wrap the selector in an object that tracks its results between runs.
  const selector = {
    // props 就是this.props,然后会调用sourceSelector进行mergeProps
    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创建元素到页面。

总结

  • provider 这个主要就是为了提供一个上下文的store,使得下级的组件不需要props可以直接获取store。

  • mapStateToProps&mapDispatchToProps

这两个就是把对象放在props里。 主要的操作就是通过wrapMapToProps.js中的wrapMapToPropsFunc函数去处理,在闭包proxy中代理我们传入的mapToProps方法去返回合并的props。 这个调用的proxy函数的是在connectAdvanced.js的makeSelectorStateful函数中,会传入一个store.getState()。至于dispatch则在initSelector初始化的时候就传递进去了。

mergeProps就是把这些props给合并起来。

  • 没有使用setState,是如何改变状态进行渲染的

    他会对connectAdvanced.js的onStateChange进行订阅,进行selector.run去对比前后的props。因为我们知道redux的dispatch代码里有执行 订阅相关的操作。 这样每次dispatch之后都会去判断shouldComponentUpdate,如果是就会去this.setState(dummyState)去render。

这里初始化mergeProps,里返回的两个函数来处理。 对应了两个函数对应了mergeProps的两种情况:

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

whenMergePropsIsOmitted方法会判断mergeProps是不是存在,如果存在就返回undefined,否则就会返回一个,可以发现,是在函数里调用的默认的mergeProps.

上面有提到createHOC对应的是connectAdvanced.然后可以看到createHOC传参的第一个是selectorFactory, 就是,然后第二个参数是一些对象。

接下来我们就可以看看对应createHOC的了。

首先看connectAdvanced在参数方面,第一个保持不变,第二个进行了扩展,添加了几个属性。 我们还是从对外暴露的接口来看,他直接暴露的是connectAdvanced方法。因为我们在createHOC所需要的是一个结果, 所以我们.

一些基本的处理,然后就是一个connect基于React.Component的继承,最后是返回return hoistStatics(Connect, WrappedComponent), 是一个类似Object.assign的copy非react的static property. 换句话说,就是把传递的容器组件自定义的静态属性附加到connect组件上去。

别急,我们慢慢的看下去。, 我们就看看这个方法是怎么处理的。, 他会根据pure属性来确定到底该如何处理。其实我们从这一段注释就可以知道了:

这里初始化结束之后,,

他也会和initSubscription一样的去判断,确定是否进行下去。. 然后去运行selector,运行selector之后,会判断shouldComponentUpdate,如果成立,则会进行forceUpdate, 注意: forceUpdate会跳过shouldComponentUpdate的判断.

createConnect方法
connect方法
createHoc
connectAdvanced
部分的源码
通过mergePropsFactories来处理
会进行处理并返回一个函数
默认的mergeProps
对应的defaultSelectorFactory
connectAdvanced方法
通过源码看看他是怎么运行的
这个hoistStatics
发现就是使用的selectorFactory方法来处理的
对外暴露的方法在这里
我们看下componentDidMount方法
下面会进行尝试订阅