按照正常来说,像 React-Redux 这一类较为活跃的社区类库,在 React 有较大的更新出现的时候通常都会及时跟进的。而这一次 React 的 Hooks 发布,有将近两个月的 beta 期,以及到截止本篇文章发布已经 Hooks 正式版也已经将近一个月来,React-Redux 到如今都没有正式发布一个相似
useRedux
这样的 Hooks API,那么这是为何呢?咱们来分析一下缘由。react
去年年末,出于兴趣,研究了一波 redux 和 react-redux 的源码,除了在原理上的理解以外,让我较为好奇的一点就是:React-Redux到目前为止都没有对 Hooks 进行支持。从使用角度上来说,出现一个相似:git
function ConnectedComponent() {
const store = useReduxStore()
const state = useRedux(function mapState()) } 复制代码
这样的代码是很是能够理解的,并且也是很是符合 Hooks 的使用习惯的,事实上社区上也出现来不少非官方的 reudx Hooks 的类库:github
说明了整体上社区对于 Hooks 的接受度是很高的,你们应该都在期待官方能给出一个真正的 Hooks API。那么为何 React-Redux 到如今都没有发布正式的 Hooks API 呢?redux
在翻阅 React-Redux 的 issues 列表的时候,我发现了这个 issue。做者很是完整得为咱们介绍了 React-Redux 从最初得 idea 到如今 v6 版本得成长历程。那么 v6 版本相比 v5 版本有哪些大的变化呢?缓存
createContext
来传递 stateProvider
订阅了 store 的变化connect
的组件传递 store 对象v6 版本更新这些内容的主要缘由以下:app
Concurrent Mode
异步渲染,若是使用老的方式可能会致使不一样的子树获取的状态不一样,使用新的 context API,React 会确保整棵树拿到的是相同的状态createContext
默认带有top-down数据流,再也不须要 React-Redux 本身实现以上是 v6 版本的变化和其缘由,可是到目前为止咱们好像并无看到任何说起 Hooks 的地方。别急,接下去就是正题了。异步
在升级到 v6 的过程当中,React-Redux 团队发现 v6 版本的总体性能是比不上 v5 的。这个性能降低的主要缘由不是 React-Redux 的实现代码有什么问题,其主要问题是来自createContent
的实现方式,以及 React-Redux 选择了只有在Provider
中订阅 store 变化。ide
注意性能
React-Redux 选择使用
createContext
和只有在Provider
中订阅都是没有任何问题的,也是 React 官方推荐的使用方法,从面向将来的眼光来看,这是势必的升级。因此同窗们在后面分析问题的时候不要问,为何不换个实现方式啥的。测试
那么所谓的性能问题具体是怎么来的呢?**主要缘由是createContext
在 value 变化的时候他是如何通知子树的。**咱们先来看一组性能测试对比图:
这个测试用例来自react-redux-benchmarks,你们有兴趣能够本身去跑一下。
从图中咱们能够看出来,v6 主要性能下降的点是来自于Scripting
,也就是运行 JavaScript 脚本的时间,从数据上来看,是 v5 版本的两倍多。虽然在Rendering
和Painting
阶段要好不少,可是由于Scripting
的占比最大,因此整体上讲是略微有些降低的。
**其根本缘由是createContext
的实现方式中,咱们更改了Provider
的 value,那么在此次更新周期中,React 会遍历Provider
的全部子节点,并对监听了这一个 context 的节点进行标记,让后续渲染中知道这个节点是须要更新的,即使他的 props 和 state 根本没有变化。**关于为何 React 要这么去实现的缘由不是一句话能讲完的,他涉及到 React 16 以后 Fiber 判断一个节点是否有更新的方法,后面我会单独写一篇文章来说解,如今你们只须要知道他就是这么实现的就能够了。
由于上诉的缘由,咱们能够想象在一个节点很是多的 React 应用中,一个相似 React-Redux 这样放置在最顶层的Provier
数据变化以后,他的整体计算量确定是很是大的。
相对的,在 v5 中由于使用老的 context API,为了不一些这个 API 带来的问题,因此 React-Redux 团队选择在connect
返回的WrapperComponent
HOC 中进行 store 数据变化的监听,也就是说 Store 变化以后以后被connect
的组件可能出现 props 上的变化,而没有任何须要遍历子树的须要。
以上就是 v6 版本在性能上不升反降的缘由。这也是 React 新的 context API 不是很适合用在变化频繁的数据上缘由。咱们能够想象若是咱们像之前同样把一个表单的全部项数据都缓存在 redux store 里面,每次输入都要更新 store,可能带来的对总体性能的影响。关于这一块,React 也有一个issue在讨论是否以及如何设计一个方案来解决这个性能上的问题。这个讨论很是热闹,你们有兴趣能够关注一下。
那么到如今为止咱们仍是没有讲到 Hooks 相关的任何内容,是否是有点偏题了?不,由于咱们已经知道了大部分的缘由,那就是新的 context API 存在的性能问题。而这个问题,反应到 Hooks 上面,则会更大程度地体现出来。
若是咱们要封装一个相似useRedux
这样的 Hook,那么咱们确定须要用到useContext
来获取Provider
提供的 state,毕竟Provider
是惟一订阅来 store 变化的。而使用了useContext
,就表明咱们这个组件是依赖于这个 context 的,也就是说一旦 state 变化,这个组件就会被标记为须要更新。
而按照咱们一直以来的使用 React-Reudx 的状况,咱们都会提供mapState
来映射组件真正须要监听的数据,由于 store 是整个应用的,不太会存在某一个组件须要整个应用全部的数据的状况。这种状况下,在 v5 版本中,甚至是在 v6 版本中使用connect
的状况,都会在 HOC 中进行mapState
的执行进行数据映射,而后经过shallowEqual
判断是否有依赖的 state 变化,若是没有实际上是不须要更新真正的组件的。
可是在使用useContext
的状况,即使咱们给useRedux
提供来mapState
,可是他的执行依然要等到这个组件真正开始执行更新的时候。也就是说咱们没法让 React 在更新这个组件以前就判断他是否能够不被更新,那么 React 提供的优化就没啥用了。
而同时一旦咱们的组件开始执行,即使咱们发现useRedux
返回的 map 以后的 state 其实跟上一次是同样的,咱们也没法告诉 React 这个组件实际上是不须要更新的来终止此次更新。因此,这是一个没法在**类库层面进行的优化。**要优化咱们只有经过使用者本身使用useMemo
这样的 API,那么对于开发经验不是那么多的同窗,极可能会致使这个组件会被频繁进行无用更新,而致使性能浪费。
那么以上就是为何 React-Redux 以及不少经常使用类库尚未更新 Hooks API 的缘由来,目前来讲这个性能问题较为无解,React 官方也在考虑是否要出一些新的 context 相关的 API 来专门优化更新频率较高的状况,咱们也只能拭目以待了。
目前来讲,若是你不清楚createContent
的这些问题,建议不要把常常须要更新的内容放在 context 里面(除非没有别的方法)。
以上,就是我对于为何 Hooks 如今呼声这么高,可是社区支持却没有这么快跟进的缘由分析,若是有任何问题,能够直接回复邮件,或者在个人AMA(Ask Me Anything)中给我提问,我都会浏览,而且进行解答。
另外在这里提出的一些问题,也会在后续进行更详细的解析:
createContext
为何在更新的时候要遍历全部子树节点我是Jocky,若是对于我分析的React内容感兴趣,能够订阅我,我会保持对React生态更新及时跟进,以及对React及其生态的内容进行深度解析。