2023再谈前端状态管理

目录

什么是状态管理?

状态

常见模式

要解决的问题

心智模型

React Context 

Context 的问题

优点

缺点

React 外部状态管理库

概览

Class 时代

Redux

单向数据流

三大原则

如何处理异步

如何处理数据间联动

优点

缺点

Dva

icestore

Mobx

设计思想

心智模型

Mobx vs Redux

优点

缺点

Hooks 时代

自下而上模式的崛起

Recoil

简介

设计思想

核心方法

优点

能力对照表

Zustand

简介

Zustand vs Redux

特点

Jotai

简介

特点

Recoil vs Jotai

Zustand vs Jotai

Valtio

简介

Valtio vs Zustand

Valtio vs Mobx

Hox

从状态管理到状态共享

优势

总结

参考资料


什么是状态管理?

状态

        状态是表示组件当前状况的 JS 对象。在 React 中,可以使用 useState 或者 this.state 维护组件内部状态,通过 props 传递给子组件使用。

        为了避免状态传递过程中出现混乱,React 引入了“单向数据流”的理念。主要思想是组件不会改变接收的数据,只会监听数据的变化,当数据发生变化时他们会使用接收到的新值,而不是修改已有的值。当组件的更新机制触发后,他们只是使用新值进行重新渲染。

        父子组件通信可以直接使用 props 和回调方式;深层次、远距离组件则要通过“状态提升”和 props 层层传递。

常见模式

React 状态管理的常见模式有:

  • 状态提升:兄弟组件间是没法直接共享状态的,可以通过将状态提升到最近的祖先组件中,所有兄弟组件就可以通过 props 一级级传递获取状态;

  • 状态组合:某些状态可能只在应用程序的特定子树中需要。最好将状态存储在尽可能接近实际需要的位置,这有助于优化渲染行为;

  • 属性下钻:将父组件的状态以属性的形式一级级显示传递给嵌套子组件;

  • Provider:React Context 通过 Provider 包裹组件,被包裹的所有嵌套子组件都可以不用通过属性下钻而是通过 context 直接获取状态。

        层层传递的 value onChange 会对一个优质代码库带来毁灭性的影响,粗暴地把数据塞在 redux 中也并不能让一个应用得到很好的拓展性和可维护性。

要解决的问题

状态管理库要解决的问题:

  1. 从组件树的「任何地方」读取存储的状态

  2. 写入存储状态的能力

  3. 提供「优化渲染」的机制

  4. 提供「优化内存使用」的机制

  5. 与「并发模式的兼容性」

  6. 数据的「持久化」

  7. 「上下文丢失」问题

  8. 「props失效」问题

  9. 「孤儿」问题

心智模型

状态更新有两种心智模型:

  • 不可变状态模型

  • 可变状态模型

  • 主要好处是可以使用原生 JS 方法;

  • 基于 Proxy 的状态管理的一个缺点是状态不可预测,难以 debug。

        因为 React 没有官方的状态管理方案,React 生态中状态管理库,百花齐放,演进出很多设计思想和心智模式。如何选择状态管理库就变得十分令人抓狂。

React Context 

        在多级嵌套组件场景下,使用“属性下钻”方式进行组件通信是一件成本极高的事情。为了解决这个问题,React 官方提供 Context 用于避免一级级属性传递。

图片

Context 的问题

        Context 存在的问题也是老生常谈。在 react 里,context 是个反模式的东西,不同于 redux 等的细粒度响应式更新,context的值一旦变化,所有依赖该 context 的组件全部都会 force update,因为 context API 并不能细粒度地分析某个组件依赖了context 里的哪个属性,并且它可以穿透 React.memo 和 shouldComponentUpdate 的对比,把所有涉事组件强制刷新。

React官方文档在 When to Use Context一节中写道:

        Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言。

        综上,在系统中跟业务相关、会频繁变动的数据在共享时,应谨慎使用 context。

        如果决定使用context,可以在一些场景中,将多个子组件依赖的不同 context 属性提升到一个父组件中,由父组件订阅 context 并以 prop 的方式下发,这样可以使用子组件的memo、shouldComponentUpdate 生效。

图片

        此外,官方文档还提到了另外一个坑,使用的时候也应该注意。

优点

  • 作为React内置的hook,不需要引入第三方库;

  • 书写还算方便。

缺点

  • Context 只能存储单一值,当数据量大起来时,你可能需要使用 createContext 创建大量 context;

  • 直接使用的话,会有一定的性能问题:每一次对 state 的某个值变更,都会导致其他使用该 state 的组件 re-render,即使没有使用该值。 你可以通过 useMemo 来解决这个问题,但是就需要一定的成本来定制一个通用的解决方案;

  • 无法处理异步请求。对于异步的逻辑,Context API 并没有提供任何 API,需要自己做封装;

  • 无法处理数据间的联动。Context API 并没有提供 API 来生成派生状态,同样也需要自行去封装一些方法来实现。

React 外部状态管理库

概览

        React 的外部状态管理库一直以来是 React 生态中非常内卷的一个领域。目前比较常见的状态管理库有 Redux(包括基于 Redux 的 Dva、Icestore)、Mobx、Zustand、Recoil、Jotai、Valtio、Hox 等。

从 npm trends 看各个状态管理库近一年的下载量趋势:

图片

        我们可以看到 Redux 作为 React 状态管理的老大哥,下载量上依然遥遥领先其他库。Mobx 作为往年热度仅次于 Redux 的状态管理库,位置正逐步有被 zustand 超越的趋势。recoil / jotai / valtio 作为这两年热门的新兴库热度也在逐步上升。hox 则处于不温不火的尴尬地位。

        将以上状态管理库按心智模型、诞生时间、star 数,绘制气泡图。以 React v16.8 版本为分水岭,状态管理库可分为 Class 时代和 Hooks 时代。Class 时代中 Redux 和 Mobx 都是非常优秀的状态库。随着 Hooks 时代的到来,状态管理的心智模型也逐步发生着演变。整体呈现从中心化到去中心化,从单一状态到原子状态,从 Provider 到拥抱 Hooks 等演变趋势。

图片

        下面,我们对上述状态管理库进行逐一对比介绍。

Class 时代

Redux

        Redux 的灵感来源于 Flux 架构和函数式编程原理,状态更新可预测、可跟踪,提倡使用「单一存储」。这通常会「导致将所有的东西存储在一个大的单体存储中」。将 UI 和远程实体状态之间的所有东西都放在一个地方管理,这变得非常难以管理。对性能造成了不小的压力。

单向数据流

他的工作流程大致如下:

  • 用户在 view 层触发某个事件,通过 dispatch 发送了 action 和 payload

  • action 和 payload 被传入 reducer 函数,返回一个新的 state

  • store 拿到 reducer 返回的 state 并做更新,同时通知 view 层进行 re-render

图片

        由此可看出 Redux 遵循“单向数据流”和“不可变状态模型”的设计思想。这使得 Redux 的状态变化是可预测、可调式的。

三大原则

此外,Redux 还遵循三大原则:

  • 1. 单一数据源

        整个应用的 全局 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。

  • 2. state 是只读的

        唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。

  • 3. 纯函数修改

        通过 reducer 修改状态,reducer 是纯函数,它接收之前的 state 和 action,并返回新的  state。记住,一定要返回一个新的对象,而不是修改之前的 state。

如何处理异步

        redux 没有规定如何处理异步数据流,最原始的方式就是使用 Action Creators,也就是在制造 action 之前进行各种的异步操作,你可以把要复用的操作抽离出来。

        当然这样并不优雅,在实际项目中我们通常使用类似 redux-thunk、redux-saga 这些中间件来支持处理异步。

如何处理数据间联动

        react-redux 的 useSelector 获取状态后,你可以编写一些逻辑来处理派生状态。如果派生状态需要复用,记得给抽离出来。

优点

  • 繁荣的社区,像不支持异步这种问题是由成熟的中间件可以解决的,你遇到的问题多多少少可以在社区找到答案;

  • 可扩展性高,中间件模式让你可以随心所欲的武装你的 dispatch;

  • 单一数据源且是树形结构,这让 redux 支持回溯,在调试上也更方便;

  • 有成熟的开发调试工具 redux devtools。

缺点

  • 陡峭的学习曲线。将副作用扔给中间件来处理,导致社区一堆中间件,学习成本陡然增加。比如处理异步请求的 Redux-saga、计算衍生状态的 reselect;

  • 大量的模版代码。使用 redux,开发者要编写大量和业务逻辑无关的模板代码,这给开发和后期维护都带来了额外的成本;

  • 大状态量情况下,性能较差。state 更新会影响所有组件。每个 action 都会调用所有 reducer;

  • reducer 要返回新的对象,如果更新的值层级较深,更新成本也很高;

  • 更多的内存占用,由于采用单一数据源,所有状态存储在一个 state 中,当某些状态不再需要使用时,也不会被垃圾回收释放内存;

        当然,redux 也在致力于解决上述缺点。比如,redux toolkit 就旨在让开发者使用标准方式编写 redux 逻辑。主要解决 redux 的 3 个问题:

  • 配置 redux store 过于麻烦;

  • 必须手动额外添加很多包才能正常使用 redux;

  • redux 需要太多模板代码。

不过,即使有 redux toolkit 的加持,redux 的学习成本依旧不低。

Dva

        dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。

Dva 的特点:

  • 易学易用,仅有 6 个 api,对 redux 用户尤其友好,配合 umi 使用后更是降低为 0 API

  • elm 概念,通过 reducers, effects 和 subscriptions 组织 model

  • 插件机制,比如 dva-loading 可以自动处理 loading 状态,不用一遍遍地写 showLoading 和 hideLoading

  • 支持 HMR,基于 babel-plugin-dva-hmr 实现 components、routes 和 models 的 HMR

        Dva 大幅降低了 Redux 的上手成本,过去也在社区拥有了拥趸,github star 数 16.1k。不过,从 2019.11 开始就没有新的版本发布,看起来已经处于不维护状态。

icestore

        icestore 是 IceJs 内置状态管理库。icestore 是面向 React 应用的、简单友好的状态管理方案。

它包含以下核心特征:

  • 简单、熟悉的 API:不需要额外的学习成本,只需要了解 React Hooks,对 Redux 用户友好。

  • 集成异步处理:记录异步操作时的执行状态,简化视图中对于等待或错误的处理逻辑。

  • 支持组件 Class 写法:友好的兼容策略可以让老项目享受轻量状态管理的乐趣。

  • 良好的 TypeScript 支持:提供完整的 TypeScript 类型定义,在 VS Code 中能获得完整的类型检查和推断。

        icestore 的灵感来自于 rematch 和 constate。整体实现和 rematch 基本一致。rematch 是一个没有模板代码的 redux 最佳实践。icestore 整体配置简单,解决了 redux 学习成本高、大量模板代码等问题,同时又很好的支持了异步处理、TypeScript 和 SSR。

IceJS 自己给出的能力对照表:

  • O: 支持

  • X: 不支持

  • +: 需要额外地进行能力扩展

功能/库reduxconstatezustandreact-trackedicestore
框架AnyReactReactReactReact
简单性★★★★★★★★★★★★★★★★
更少的模板代码★★★★★★★★★★★★
可配置性★★★★★★★★★★★★★★★★
共享状态OOOOO
复用状态OOOOO
状态联动++++O
Class 组件支持O+++O
Function 组件支持OOOOO
异步更新的状态+XXXO
SSROOXOO
持久化+XXX+
懒加载模型++++O
中心化+XXXO
中间件或插件机制OXOXO
开发者工具OXOXO

Mobx

设计思想

        MobX 的主要思想是用「函数响应式编程」和「可变状态模型」使得状态管理变得简单和可扩展。

MobX背后的哲学很简单:

        任何源自应用状态的东西都应该自动地获得。其中包括UI、数据序列化、服务器通讯等等。

        React 和 MobX 是一对强力组合。React 通过提供机制把应用状态转换为可渲染组件树并对其进行渲染。而 MobX 提供机制来存储和更新应用状态供 React 使用。

        对于应用开发中的常见问题,React 和 MobX 都提供了最优和独特的解决方案。React 提供了优化UI渲染的机制, 这种机制就是通过使用虚拟DOM来减少昂贵的DOM变化的数量。MobX 提供了优化应用状态与 React 组件同步的机制,这种机制就是使用响应式虚拟依赖状态图表,它只有在真正需要的时候才更新并且永远保持是最新的。

心智模型

Mobx 的心智模型和 react 很像,它区分了应用程序的三个概念:

  • State(状态)

  • Actions(动作)

  • Derivations(派生)

        首先创建可观察的状态(Observable State),通过 Action 更新 State,然后自动更新所有的派生(Derivations)。派生包括Computed value(类似 useMemo 或 useSelector)、副作用函数(类似 useEffect )和UI(render)。

图片

        Mobx 虽然心智模型像 react,但是实现却是完完全全的 vue:mutable + proxy(为了兼容性,proxy 实际上使用 Object.defineProperty 实现)。

使用反 react 的数据流模式,注定会有成本:

  • Mobx 的响应式脱离了 react 自身的生命周期,就不得不显式声明 其派生的作用时机和范围。比如副作用触发需要在 useEffect 里再跑一个 autorun/reaction,要给 DOM render 包一层 useObserver/Observer,都加大了开发成本。

  • Mobx 会在组件挂载时收集依赖,和 state 建立联系,这个方式在即将到来的 react 18 的并发模式(Concurrent Mode)中,可能无法平滑地迁移。为此,react 专门开发了create-subscription 方法用于在组件中订阅外部源,但是实际的应用效果还未可知。

尤大本人也盖过章:React + MobX 本质上就是一个更繁琐的Vue。

Mobx vs Redux

Mobx 和 Redux 的对比,实际上可以归结为 面向对象 vs 函数式 和 Mutable vs Immutable。

  • 相比于 redux 的广播遍历 dispatch,然后遍历判断引用来决定组件是否更新,mobx 基于 proxy 可以精确收集依赖、局部更新组件(类似vue),理论上会有更好的性能,但 redux 认为这可能不是一个问题(Won't calling “all my reducers” for each action be slow?)

  • Mobx 因为数据只有一份引用,没有回溯能力,不像 redux 每次更新都相当于打了一个快照,调试时搭配 redux-logger 这样的中间件,可以很直观地看到数据流变化历史。

  • Mobx 的学习成本更低,没有全家桶。

  • Mobx 在更新 state 中深层嵌套属性时更方便,直接赋值就好了,redux 则需要更新所有途经层级的引用(当然搭配 immer 也不麻烦)。

优点

  • 简单易用,没有模板代码;

  • 精准更新,性能更好;

缺点

  • 难以调试。由于采用可变状态模型,状态不可预测和追溯,难以 debug;

  • 太过灵活,更容易导致 bug;

  • 响应式是基于 Proxy 实现的,希望传递的是一个数组,拿到的却是一个 Proxy。排查问题时有点痛苦。

Hooks 时代

        Hooks  是 React 16.8 新增的特性,使得我们可以在函数组件中使用 state 以及其他 React 特性。

Hooks 的引入主要是为了解决 React Class 组件的以下问题:

  • 在组件之间复用状态逻辑很难

        Class 组件会将视图和状态逻辑糅杂在一起,如果想复用组件中的状态逻辑,需要使用 render props 和 高阶组件,但是这类方案需要重新组织组件结构,会形成组件的嵌套地狱,代码逻辑也会变得难以理解。

  • 复杂组件的理解成本很高

        Class 组件的状态逻辑会充斥在各个生命周期中,完全不相关的代码出现在同一个生命周期函数中,逻辑难以理解,容易引发 bug,且在多数情况下,很难将组件拆分成更小的粒度。

Hooks 是一种开发理念和组织理念的革新,有 3 个特性:

  • primitive。元数据化,将混沌的 state 打散为一个个元数据;

  • decentralization。去中心化,Class 时代的理念是“顶层下发”,Hooks 带来了强烈的“组件自治”理念;

  • algebraic effects。代数效应,剥离组件中的副作用,让开发者更专注业务逻辑。

代数效应是函数式编程中的一个概念,用于将副作用从函数调用中分离。

自下而上模式的崛起

        我们可以看到以前的状态管理解决方案,如Redux,设计理念是状态 「自上而下」流动。它「倾向于在组件树的顶端吸走所有的状态」。状态被维护在组件树的高处,下面的组件通过选择器拉取他们需要的状态。

        在新的组件构建理念中,一种「自下而上」的观点对构建具有组合模式的应用具有很好的指导作用。

        而 hook 就是这种理念的践行者,即把可组合的部件放在一起形成一个更大的整体。

        通过 hook,我们可以从具有巨大全局存储的「单体状态管理」转变为向自下而上的 「微状态管理」,通过hook消费更小的状态片。

        像接下来要介绍的 Recoil 和 Jotai 这样的流行库以其 「原子状态」的概念体现了这种自下而上的理念。「原子是一个最小但完整的状态单位」。它们是小块的状态,可以连接在一起形成新的衍生状态。最终形成了一个应用状态图。

        这个模型允许你自下而上地建立起「状态图」。并通过仅使图中已更新的原子失效来优化渲染。

        这与拥有一个大的单体状态球形成鲜明对比,你可以「订阅并试图避免不必要的渲染」。

        接下来我们要介绍 5 个 Hooks 时代的状态库,分别是 recoil、zustand、jotai、valtio、hox。比较有趣的是其中 3 个都是 Daishi Kato开发的,采用了不同的设计思想,但是都在短期内取得不错的社区热度,这 3 个库分别是 zustand、jotai、valtio,这三个词其实是“状态”在 3 种语言中的不同发音。

        zustand 🇩🇪 德语 "状态",jotai 🇯🇵 日语 "状态"、valtio 🇫🇮 芬兰语 "状态"。

Recoil

简介

        Recoil 是在React Europe 2020 Conference上 facebook 官方推出的专为 react 打造的状态管理库,动机是解决 react 状态共享模式的局限性:

  • 以往只能将 state 提升到公共祖先 来实现状态共享,并且一旦这么做了,基本就无法将组件树的顶层(state 必须存在的地方)与叶子组件 (使用 state 的地方) 进行代码分割

  • Context 只能存储单一值,无法存储 多个各自拥有消费者的值 的集合

设计思想

        Recoil的状态集是一个有向图 (directed graph),正交且天然连结于React组件树。状态的变化从该图的顶点(atom)开始,流经纯函数 (selector) 再传入组件。

图片

        Recoil 定义了一个有向图 (directed graph),正交同时又天然连结于你的 React 树上。状态的变化从该图的顶点(我们称之为 atom)开始,流经纯函数 (我们称之为 selector) 再传入组件。基于这样的实现:

  • 我们可以定义无需模板代码的 API,共享的状态拥有与 React 本地 state 一样简单的 get/set 接口 (当然如果需要,也可以使用 reducer 等进行封装);

  • 我们有了与 Concurrent 模式及其他 React 新特性兼容的可能性;

  • 状态的定义是渐进式和分布式的,这使代码分割成为可能;

  • 无需修改对应的组件,就能将它们本地的 state 用派生数据替换;

  • 无需修改对应的组件,就能将派生数据在同步与异步间切换;

  • 我们能将导航视为头等概念,甚至可以将状态的转变编码进链接中;

  • 可以很轻松地以可回溯的方式持久化整个应用的状态,持久化的状态不会因为应用的改变而丢失。

正交:相互独立,相互间不可替代,并且可以组合起来实现其它功能

        Recoil 每一次状态变更都会生成一个不可变的快照,利用这个特性,可以快速实现应用导航相关的功能,例如状态回溯、跳转等。

核心方法

Recoil中定义状态的两个核心方法:

  • atom: 定义原子状态,即组件的某个状态最小集,

  • selector: 定义派生状态,其实就是 computed value

        消费状态的方法有 useRecoilState、useRecoilValue、useSetRecoilState 等,用法和 react 的 useState 类似,几乎没有上手成本。另外值得注意的是,recoil 目前只支持 FC 的 hook 用法,Class 组件想用的话可以通过 HOC 的方式获取状态并注入组件。

优点

  • 状态原子化(atom),自由组合和订阅;并且状态定义是渐进式和分布式的,使代码分割成为可能;

  • 没有模板代码,天然是 hook 模式,让 react 尽量保持原来的样子;

  • 兼容并发模式(Concurrent Mode);

  • 提供对状态流的快照(snapshot)支持,可以轻松回溯应用状态,甚至将 snopshot 编码放进 url,让任何人打开应用都能进入到同样的状态;

能力对照表

方案学习成本编码成本TS友好SSRCode Split并发模式兼容可调试性生态繁荣
Redux一般支持不支持支持
Mobx支持支持未知
Recoil实践较少支持支持

Zustand

简介

        zustand 是一个轻量级状态管理库,和 redux 一样都是基于不可变状态模型和单向数据流的,状态对象 state 不可被修改,只能被替换。渲染优化要手动通过 selectors 进行。

Zustand vs Redux

  • zustand 和 redux 是非常像的,都基于不可变状态模型,都基于单向数据流。

  • 不过,redux 需要应用被 Context Provider 包裹,zustand 则不需要。

  • 二者更新数据的方式不同,redux 基于 reducers,更新状态的 reducers 是严格的方法,这就使得状态更加可预测。zustand 不使用 reducers 而是通过更灵活的方法来更新状态。

特点

zustand 的特点:

  • 轻量级;

  • 中心化,单一 store;

  • 不可变状态模型;

  • 不固执。很少限制,非常开放。

Jotai

简介

        jotai 是一个小型全局状态管理库,它模仿了 useState、useReducer。jotai 有个叫做 atom 的概念,用于表示小的状态片段。和 zustand 不同的是,他是一个组件级别的状态管理库。和 zustand 相同的是同样都基于不可变状态模型。

        jotai 是 Context 和订阅机制的结合,是面向 React 的一种全局状态管理库。如果你的需求是一个没有额外重复渲染的 Context,那 jotai 是个不错的选择。

特点

jotai 有两个特点:

  • 语法简单

  • jotai 的状态不是全局状态

        atom 可以在 React 组件的生命周期里创建和销毁。这通过多个 Context 是无法实现的,因为使用 Context 增加一个新的 state 意味着增加一个新的 Provider 组件,如果新增一个组件,它所有的子组件都会被重新挂载,会失去所有状态。

衍生 atom:

        atom 可以像积木一样被组合,生成新的 atom,从而实现复杂逻辑。

        jotai 通过 atom 之间的依赖来实现自动渲染优化。

推荐场景:组件为中心的应用。

Recoil vs Jotai

jotai 深受 recoil 启发,设计理念基本相同。但有以下不同:

  • 最大的不同是是否需要键字符串,开发 jotai 的一大动力就是要省略键字符串。因为键属性必须是唯一的,键命名是一项艰巨的任务;

  • 另一个不同是 jotai 不需要使用 Provider 包裹组件,这对开发者来说可以大幅降低开发成本和心理负担。

Zustand vs Jotai

Zustand 和 Jotai 之间有两个主要不同:

  • Zustand 是单一 store,Jotai 由原子 atom 组合而成;

  • Zustand 的 store 存储在 React 外部,Jotai 的 store 存储在 React 内部。

Valtio

简介

        基于可变状态模型,利用 Proxy 获取一个和 React 集成在一起的不可变快照。

        利用 Proxy 自动进行重新渲染优化,这个过程使用了状态使用跟踪技术。通过状态使用跟踪,可以检测到状态的哪部分被使用,让组件实现按使用重新渲染。同时,开发者也可以编写更少的代码。

Valtio vs Zustand

        zustand 基于不可变状态模型,valtio 基于可变状态模型。

        valtio 通过属性访问自动进行渲染优化,zustand 推荐使用 selectors 手动进行渲染优化。

Valtio vs Mobx

        渲染优化上,valtio 使用 hook,mobx 使用高阶组件。

Hox

从状态管理到状态共享

        redux、zustand、recoil 这些状态管理库,它们虽然在一定程度上也可以帮我们解决数据共享的问题,但它们最本质的能力还是对数据的操作。它们被称做也确实应该被称做"状态管理"工具。

        而 Hox 想解决的问题,不是如何组织和操作数据,不是数据流的分层、异步、细粒度,我们希望 Hox 只聚焦于一个痛点:在多个组件间共享状态。

        如果你也意识到了,层层传递的 value onChange 会对一个优质代码库带来的毁灭性影响,粗暴地把数据塞在 redux 中也并不能让一个应用得到很好的拓展性和可维护性,那么 Hox 或许会是一个适合你的"状态共享"方案,它简单、轻量、可靠,适合无论大小的几乎所有项目。

优势

  • 直接复用已有的 React 知识,几乎没有学习成本,你怎么写 React 组件,就可以怎么写 Store

  • 为灵活重构而设计,在 Store 和组件中使用同一套 DSL 可以让你几乎 0 成本的将组件的局部状态转化为一个组件间共享的状态

  • 同时支持局部状态和全局状态,在灵活和简单之间做到了很好的平衡

  • 优秀的性能和 TypeScript 支持

总结

        简单场景使用原生的useState、useReducer、useContext就能满足;还可以用 Hox这样小而美的库将 hook 的状态直接拓展成持久化状态,几乎没有额外的心智负担。

        复杂场景的应用,redux、mobx都是经受过千锤百炼的库,社区生态也很完备。

        Redux高度模板化、分层化,职责划分清晰,塑造了其状态在可回溯、可维护性方面的优势;搭配thunk、saga这些中间件几乎是无所不能。

        Mobx的优势是写法简单和高性能,但状态的可维护性不如redux,在并发模式中的兼容性也有待观察。

        随着hook和有官方背景的 recoil 的出现,状态管理似乎在朝原子化、组件化的方向发展,这也符合react的组件化哲学。Redux的暴力遍历和分发或许已经是逆潮流的解法。

        没有最好的状态管理库,只有最合适的状态管理库。

详细状态库能力对照表:

Class 时代Hooks 时代
传统流派响应式流派原子状态流派传统流派响应式流派原子状态流派
reduxdvajsicestore(ice内置)mobxrecoilzustandValtioJotaihox
STAR数59k16.1k38726k18.1k24.5k5.9k11.3k1.3k
诞生时间20112016.122019.32015.3201920182020.112020.32019.1
特性全局一棵状态树,利用context,通过action 触发变动,reducer纯函数修改store,最后导致props 变动,进而组件重新渲染深度整合 redux、redux-saga,便于 redux 用户快速切换类 dvajs,创造 icestore 的灵感来自于 rematch 和 constate。基于ES6 proxy ,使用观察者/可观察模式的,当你修改一个值时,任何使用该值的组件都会自动重新渲染。原子化管理状态,进行精确渲染。使用 Recoil 会为你创建一个数据流向图,从 atom(共享状态)到 selector(纯函数),再流向 React 组件。Atom 是组件可以订阅的 state 单位。selector 可以同步或异步改变此 state。基于观察者/可观察模式,内部对通过 state 绑定的组件,添加到了订阅者队列,store中的属性相当于一个被观察者,当属性状态变更后,通知所有订阅了该数属性的组件进行更新利用 Proxy 自动进行重新渲染优化,这个过程使用了状态使用跟踪技术。通过状态使用跟踪,可以检测到状态的哪部分被使用,让组件实现按使用重新渲染。同时,开发者也可以编写更少的代码。jotai 是一个小型全局状态管理库,它模仿了 useState、useReducer。jotai 有个叫做 atom 的概念,用于表示小的状态片段。和 zustand 不同的是,他是一个组件级别的状态管理库。和 zustand 相同的是同样都基于不可变状态模型。类似于自定义hook,利用useState 能力,state状态变化触发组件重新渲染
学习成本很高
使用成本很高,模板代码多低,仅有6个API
Typescript友好友好不友好,没有清晰的依赖关系,类型支持很差友好友好友好友好友好友好友好
状态拆分react/toolkit createSlice支持支持支持支持支持支持支持支持
性能中等中等中等中等
异步支持不友好友好友好友好友好友好友好友好友好
React concurrent 模式支持支持支持支持支持支持支持支持
易于调试
devtools
SSR支持支持支持支持,但可能不健全支持,但可能不健全支持,但可能不健全支持,但可能不健全不支持

参考资料

  • Do React Hooks Replace Redux?

  • React 状态管理工具:我是这样选择的

  • You Might Not Need Redux

  • 各流派 React 状态管理对比和原理实现

  • 盘点React常见的状态管理方式

  • 2022 年,我们再来谈谈 React 状态管理

  • DvaJS

  • 支付宝前端应用架构的发展和选择

  • HoxJS

  • Redux vs Zustand

  • React 状态管理新浪潮

  • React-全局状态管理的群魔乱舞

  • 各流派 React 状态管理对比和原理实现 - mdnice 墨滴

  • 自述 · Redux

  • Recoil - Facebook 官方 React 状态管理器

  • [译] React 状态管理的前世,今生和未来 - 开发者头条

  • 2021年的React状态管理 - 掘金

  • 状态管理方案发展概览

  • State Management: Overview | React Common Tools and Practices

  • Blogged Answers: Why React Context is Not a “State Management” Tool (and Why It Doesn’t Replace Redux)

  • zustand vs redux

  • hox - 下一代 React 状态管理器

  • MobX vs Redux: Evaluating Two Popular Libraries For State Management | SPEC INDIA

  • React 状态管理的新浪潮

  • Redux 的性能问题

  • mobx vs redux

  • zustand vs jotai vs valtio

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/77138.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

危大工程智慧工地源码,微服务+Java+Spring Cloud +UniApp +MySql 物联网、人工智能、视频AI分析

一套智慧工地管理平台源码,PC端移动APP端可视货数据管理端源码 智慧工地可视化系统利用物联网、人工智能、云计算、大数据、移动互联网等新一代信息技术,通过工地中台、三维建模服务、视频AI分析服务等技术支撑,实现智慧工地高精度动态仿真&a…

windows上给oracle打补丁注意事项

打补丁的过程 1、升级opatch工具,检查剩余空间用于存放ORACLE_HOME的备份,设置oracle_home环境变量,通过readme中的先决条件来检查现有补丁是否和本次补丁冲突 2、opatch apply 升级数据库软件,这个必须数据库文件不要被进程调用 在windows上…

美团前端研发框架Rome实践和演进趋势

本文整理自美团技术沙龙第76期《大前端研发协同效能提升与实践》,为大家介绍了美团到店前端研发框架Rome实践和演进趋势。 具体来讲,本文首先介绍了Rome整体的工程生态、演变路径、规模化升级以及工程框架外的开发辅助工具;第二部分&#xff…

GoogLeNet卷积神经网络-笔记

GoogLeNet卷积神经网络-笔记 GoogLeNet是2014年ImageNet比赛的冠军, 它的主要特点是网络不仅有深度, 还在横向上具有“宽度”。 由于图像信息在空间尺寸上的巨大差异, 如何选择合适的卷积核来提取特征就显得比较困难了。 空间分布范围更广的…

matlab智能算法程序包89套最新高清录制!matlab专题系列!

关于我为什么要做代码分享这件事? 助力科研旅程! 面对茫茫多的文献,想复现却不知从何做起,我们通过打包成品代码,将过程完善,让您可以拿到一手的复现过程以及资料,从而在此基础上,照…

OpenCV基础

目录 图像基本操作数据读取——图像数据读取——视频截取部分图像数据 ROI——Region of Interest颜色通道提取边界填充数值计算图像融合 图像处理灰度图HSV图像阈值图像平滑(滤波)形态学-腐蚀操作形态学-膨胀操作开运算与闭运算梯度运算礼帽与黑帽 图像梯度——Sobel算子看看L…

Vue 2.x 项目升级到 Vue 3详细指南【总结版】

文章目录 0.前言1.升级教程1.1. 升级 Vue CLI:1.2. 安装 Vue 3:1.3. 更新 Vue 组件:1.4. 迁移全局 API:1.5. 迁移路由和状态管理器:1.6. 迁移 TypeScript:1.7. 迁移测试代码: 2.迁移总结2.0. 这…

深入学习 Redis - 谈谈你对 Redis 的 RDB、AOF、混合持久化的了解吧?

目录 一、Redis 是怎么存储数据的? 二、Redis 具体是按照什么样的策略来实现持久化的? 2.1、RDB(Redis Database) 2.1.1、触发机制 2.1.2、bgsave 命令处理流程 2.1.3、RDB 文件的处理 2.1.4、演示效果 1)手动执…

STL容器适配器 -- stack和queue(使用+实现)(C++)

stack和queue stackstack的介绍stack的使用stack的实现 queuequeue的介绍queue的使用queue的实现 deque简单介绍deque(双端队列)双开口连续打引号的原因 deque底层结构deque的迭代器封装结构(复杂)deque的优缺点 栈和队列数据结构…

Java版Spring Cloud+Spring Boot+Mybatis+uniapp知识付费平台讲解+免费搭建 qt

Java版知识付费源码 Spring CloudSpring BootMybatisuniapp前后端分离实现知识付费平台 提供职业教育、企业培训、知识付费系统搭建服务。系统功能包含:录播课、直播课、题库、营销、公司组织架构、员工入职培训等。 提供私有化部署,免费售…

《面试1v1》Kafka的ack机制

🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪 🍅 技术交流:定期更新Java硬核干货,不定期送书活动 🍅 王哥多年工作总结:Java学习路线总结&#xf…

P25透明屏:探究在商业广告领域的应用表现

P25透明屏是一种新型的显示屏技术,具有高透明度和高分辨率的特点。 它可以将图像或视频直接投影到透明的表面上,使得观众可以透过屏幕看到背后的景物,同时也能够看到屏幕上的内容。 P25透明屏广泛应用于商业展示、户外广告、产品展示等领域…

年薪930万,谷歌薪资大揭秘

硅谷大厂中,谷歌员工称得上是科技行业中收入最高的一些人。 据统计,谷歌工程师在2022年总薪酬中位数为279,802美元(约200万人民币),但这仅是基本工资。 如果计入股权和奖金,他们的收入甚至更高。 近来&am…

PHP-Mysql好运图书管理系统--【白嫖项目】

强撸项目系列总目录在000集 PHP要怎么学–【思维导图知识范围】 文章目录 本系列校训本项目使用技术 首页必要的项目知识ThinkPHP的MVCThinkTemplateThinkPHP 6和ThinkPHP 5 phpStudy 设置导数据库前台展示页面后台的管理界面数据库表结构项目目录如图:代码部分&a…

Mybatis引出的一系列问题-Spring事务的探究

1 spring事务的传播特性 package com.zs.service;Service public class UserService {Autowiredprivate UserDao userDA0;Transactionalpublic void transfer(String fromName, String toName, Integer money) {userDA0.out(fromName, money);int a 1 / 0;userDA0.in(toName,…

[CKA]考试之一个 Pod 封装多个容器

由于最新的CKA考试改版,不允许存储书签,本博客致力怎么一步步从官网把答案找到,如何修改把题做对,下面开始我们的 CKA之旅 题目为: Task 创建一个Pod,名字为kucc1,这个Pod包含4容器&#xff…

思科模拟器配置静态路由(下一跳使用IP)

Router0配置代码:##端口配置 Router(config)#int fastEthernet 0/0 Router(config-if)#ip address 192.168.10.254 255.255.255.0 Router(config-if)#no shutdown Router(config-if)#int fastEthernet 0/1 Router(config-if)#ip address 192.168.20.1 255.255.255.2…

pycharm——树状图

from pyecharts import options as opts from pyecharts.charts import Treedata [{"children": [{"name": "计算机"},{"children": [{"children": [{"name": "主机"}], "name": "硬盘…

真机搭建中小网络

这是b站上的一个视频,演示了如何搭建一个典型的中小网络,供企业使用 一、上行端口:上行端口就是连接汇聚或者核心层的口,或者是出广域网互联网的口。也可理解成上传数据的端口。 二、下行端口:连接数据线进行下载的端…

一百四十二、Linux——查看Linux服务器架构的版本类型

一、目的 查看已经安装好的Linux服务器架构的版本类型,看服务器版本是32位还是64位 而且可以区分出是kettle的文件x86或x86_64,x86是32位,而x86_64是64位 注意: 32位的查询结果为i386、i686 64位的查询结果为x86_64 二、Linu…