资讯专栏INFORMATION COLUMN

[源码解读] react-redux

Olivia / 2978人阅读

摘要:本文并不逐行地对源码进行细致分析,不如说是基于以下几个问题,对源码进行大致的扫览。我们已经知道,中,允许用户注册监听器,这些监听器会在每次执行结束后遍历触发。省略一些无关代码其中,是为了在嵌套的中嵌套执行。

react-redux 源码解读

[TOC]

前置知识

阅读本篇文章前,请先确认你是否了解以下知识:

react

redux

高阶组件

react diff 机制

其中高阶组件如果你不太了解也无所谓,你只需要知道,高阶组件就是一个工厂函数,它接收一个组件类(或者函数组件),返回一个被修改后的新的组件类。connect 就是一个高阶组件。

文章内会使用的简写

hoc: 高阶组件(higher order component)

scu: shouldComponentUpdate

Issues

我们知道,react-redux 为开发者提供了 redux 到 react 的 binding。本文并不逐行地对源码进行细致分析,不如说是基于以下几个问题,对源码进行大致的扫览。

我们把关注点放在:

connect 是通过什么方式来连接 store 的?

怎么分发 state tree 到子组件的?
provider 仅仅只是保持了一个 store 实例,即使 store 中的 state tree 变化了,由于 react diff 阶段只做浅比较,仅比较对象引用,故 provider.props.store 被视为未发生变化,那就无法把新的 state tree 分发到子组件了。

我们知道 connect 还接收若干配置函数,用来 mapXxToProps ,以及设置是否进行 pure 优化等。这些没什么意思,我们就不 care 了。

两个核心 api

react-redux 只有两个核心的部分

Provider

connect

Provider

Provider 的作用仅仅是维护一个 store 实例,并使用 context , 为分发 state tree 提供了机制

connect

connect 顾名思义,连接 container 和 store ,使得 container 能响应 state tree 的变化。

connect 本身是一个高阶组件,它调用了 connectAdvanced 这么一个工厂函数。

而上面提到的两个问题,全都由 connectAdvanced 封装了。

connectAdvanced

connectAdvanced 是一个高阶组件的工厂函数,它返回一个 hoc (高阶组件),这个 hoc 最终返回一个 Connect(WrappedComponent) 的组件。

connect 是通过什么方式来连接 store 的?

在这个 Connect(WrappedComponent) 中,声明了 getChildContext 函数,通过它来获取到 Provider 中共享的 store 实例。

NOTE : 理论上,Provider 下任何一个子组件,只要我们也去声明了 getChildContext ,那么它就和 connect 了一样,可以得到 store 实例了。

connect 怎么分发 state tree 到子组件的?

一句话,主要通过 store.subscribe 接口和组件 props 变更自动 re-render 机制。

在 react-redux 中,我们定义了这么几个东西,帮助我们来做到上面几件事情。

selector

subscriptions

每个 Connect(WrappedComponent) 在构造函数中都需要 init 上述两个实体。

selector

selector 主要由 selectorFactory 构造,selector 内部包含

一个计算新 props 的生成函数
sourceSelector(store.getState(), props)
其中 props 是 selector 之前缓存的,后续会直接拿来和 store.getState() 比较引用

一个 shouldComponentUpdate 的标识
Connect(WrappedComponent) 内部是直接使用它来判断 scu 的

shouldComponentUpdate() {
  return this.selector.shouldComponentUpdate
}    

源码如下

const selector = {
    // 实际 run 的逻辑又是在 SelectorFactory 里定义的,这里只是做 scu
    run: function runComponentSelector(props) {
      try {
        const nextProps = sourceSelector(store.getState(), props)
        // connect 的 scu 实际逻辑在这里定义了
        // 比较 run 之后两次 props 的引用是否仍然保持相等
        if (nextProps !== selector.props || selector.error) {
          selector.shouldComponentUpdate = true
          selector.props = nextProps
          selector.error = null
        }
      } catch (error) {
        selector.shouldComponentUpdate = true
        selector.error = error
      }
    }
  }

调用 selector.run 的时候,主要做了两件事情

根据之前缓存的 prevProps 和当前 redux store 实例中的 statet tree 生成新的 props。

通过比较引用,更新 shouldComponentUpdate 判断标识

Connect(WrappedComponent) 会在三处调用 selector.run

componentDidMount

componentWillReceiveProps

onStateChanged

之后 React 就会自动向子组件分发变更后的 props ,实现 re-render。

subcription

我们在上面提到过 Provider 本身是无法通知 state tree 的变化的,于是为了监听 state tree 变化,我们需要通过 store.subcribe 接口,来向 Provider 的 store 实例注册一些监听器。

我们已经知道,redux 中,store.subcribe 允许用户注册监听器,这些监听器会在每次 dispatch 执行结束后遍历触发。

于是在 react-redux 中, Connect(WrappedComponent) 中的 subcription 最终就是被注册到 store 中。

initSubscription() {
    // ...省略一些无关代码
    this.subscription = new Subscription(this.store, parentSub, this.onStateChange.bind(this))
}

其中,

parentSub 是为了在嵌套的 connect 中嵌套执行 subscription。

new Subscription 主要构造了一个包含 trySubscribe 方法的对象

trySubscribe() {
    if (!this.unsubscribe) {
      this.unsubscribe = this.parentSub
        ? this.parentSub.addNestedSub(this.onStateChange)
        : this.store.subscribe(this.onStateChange)

      this.listeners = createListenerCollection()
    }
  }

Connect(WrappedComponent)componentDidMounttrySubscribe,在 componentWillUnmounttryUnsubscribe

而 trySubscribe 的主要逻辑就是将 onStateChange 注册到 redux.store 中。

而我们已经说过,onStateChange 主要就是执行 selector.run

onStateChange() {
  // 得到新的 props 和 state
  this.selector.run(this.props)

  if (!this.selector.shouldComponentUpdate) {
    this.notifyNestedSubs()
  } else {
    this.componentDidUpdate = this.notifyNestedSubsOnComponentDidUpdate
    this.setState(dummyState)
  }
}

于是,每次 state tree 发生变化后,或者更准确地说,每次 dispatch 成功后,redux.store 都会通过注册好的 subscription,执行 Connect(WrappedComponent) 中的 onStateChange ,再由 selector.run 来判断是否需要生成新的 props。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/87218.html

相关文章

  • react-redux用法及源码解读

    摘要:带有内部状态,内部可以使用。代表监听的全局,也可以说是全局的,表示该组件自身携带的方法。回答问题是通过应用的,将通过顶层组件传递到上下文环境中。所有页面集合缓存不用每次都重新加载通过绑定用法完参考文章 react-redux把组件分为UI组件和容器组件。先看图下图: showImg(https://segmentfault.com/img/bVbkVBz?w=1378&h=1194);...

    Zoom 评论0 收藏0
  • dva系列源码解读

    摘要:介绍概述本次对源码的解读除了传统的从入手外还将引入带入问题读源码的理念,因为只有这样当读完源码之后才会有切身的收获。 介绍 概述 本次对 dva 源码的解读除了传统的从 api 入手外还将引入带入问题读源码的理念,因为只有这样当读完源码之后才会有切身的收获。另外除了 dva 的源码外还会解读一些常用的 dva 插件的源码。 电子书 https://dva-source-docs.net...

    focusj 评论0 收藏0
  • Redux系列源码解读

    摘要:源码解读的方法就是给我们提供了灵活的创建符合标准的的方法。用于分解树,每一个对应的一个对应的子。这样在将传递给时利于分解。源码实现了这种将函数数组,通过的方法,实现层层嵌套的执行,达到中间件的实现。 Redux 源码解读 1.redux-action createAction redux-action的createAction方法就是给我们提供了灵活的创建符合FSA标准的actio...

    Scliang 评论0 收藏0
  • 带着问题看 react-redux 源码实现

    摘要:更新类型及具体内容负责更新数据的具体逻辑。即根据当前及携带的信息合成新的数据。下面带着这些问题深入了解本质上也是高阶组件的一种实现。核心实现声明以被子组件获取。通过的实现,我们可以得到的重要性浅比较的实现由此可以看到的重要性。前言 Redux作为通用的状态管理器,可以搭配任意界面框架。所以并搭配react使用的话就要借助redux官方提供的React绑定库react-redux,以高效灵活的...

    JellyBool 评论0 收藏0
  • 重读redux源码(二)

    摘要:函数组合,科里化的串联结合示例源码,实现也很优雅,对于返回的,将等参数传递进去,然后执行,等待回调异步完成再。对于正常对象则进行下一步。前言 作为前端状态管理器,这个比较跨时代的工具库redux有很多实现和思想值得我们思考。在深入源码之前,我们可以相关注下一些常见问题,这样带着问题去看实现,也能更加清晰的了解。 常见问题 大概看了下主要有这么几个: redux三大原则 这个可以直接参考...

    dingda 评论0 收藏0

发表评论

0条评论

Olivia

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<