前端开发:什么是状态管理及React Context 

2023-07-0714:37:08WEB前端开发Comments1,450 views字数 11151阅读模式

什么是状态管理?

状态

状态是表示组件当前状况的 JS 对象。在 React 中,可以使用 useState 或者 this.state 维护组件内部状态,通过 props 传递给子组件使用。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

为了避免状态传递过程中出现混乱,React 引入了“单向数据流”的理念。主要思想是组件不会改变接收的数据,只会监听数据的变化,当数据发生变化时他们会使用接收到的新值,而不是修改已有的值。当组件的更新机制触发后,他们只是使用新值进行重新渲染。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

父子组件通信可以直接使用 props 和回调方式;深层次、远距离组件则要通过“状态提升”和 props 层层传递。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

常见模式

React 状态管理的常见模式有:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • 状态提升:兄弟组件间是没法直接共享状态的,可以通过将状态提升到最近的祖先组件中,所有兄弟组件就可以通过 props 一级级传递获取状态;
  • 状态组合:某些状态可能只在应用程序的特定子树中需要。最好将状态存储在尽可能接近实际需要的位置,这有助于优化渲染行为;
  • 属性下钻:将父组件的状态以属性的形式一级级显示传递给嵌套子组件;
  • Provider:React Context 通过 Provider 包裹组件,被包裹的所有嵌套子组件都可以不用通过属性下钻而是通过 context 直接获取状态。

层层传递的 value onChange 会对一个优质代码库带来的毁灭性影响,粗暴地把数据塞在 redux 中也并不能让一个应用得到很好的拓展性和可维护性。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

要解决的问题

状态管理库要解决的问题:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  1. 从组件树的「任何地方」读取存储的状态
  2. 写入存储状态的能力
  3. 提供「优化渲染」的机制
  4. 提供「优化内存使用」的机制
  5. 与「并发模式的兼容性」
  6. 数据的「持久化」
  7. 「上下文丢失」问题
  8. 「props失效」问题
  9. 「孤儿」问题

心智模型

状态更新有两种心智模型:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • 不可变状态模型
  • 可变状态模型
  • 主要好处是可以使用原生 JS 方法;
  • 基于 Proxy 的状态管理的一个缺点是状态不可预测,难以 debug。

因为 React 没有官方的状态管理方案,React 生态中状态管理库,百花齐放,演进出很多设计思想和心智模式。如何选择状态管理库就变得十分令人抓狂。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

React Context

在多级嵌套组件场景下,使用“属性下钻”方式进行组件通信是一件成本极高的事情。为了解决这个问题,React 官方提供 Context 用于避免一级级属性传递。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

前端开发:什么是状态管理及React Context 文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Context 的问题

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

React官方文档在 When to Use Context一节中写道:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

综上,在系统中跟业务相关、会频繁变动的数据在共享时,应谨慎使用 context。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

如果决定使用context,可以在一些场景中,将多个子组件依赖的不同context属性提升到一个父组件中,由父组件订阅context并以prop的方式下发,这样可以使用子组件的memo、shouldComponentUpdate生效。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

前端开发:什么是状态管理及React Context 文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

此外,官方文档还提到了另外一个坑,使用的时候也应该注意。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

优点

  • 作为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 等。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

从 npm trends 看各个状态管理库近一年的下载量趋势:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

前端开发:什么是状态管理及React Context 文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

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

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

前端开发:什么是状态管理及React Context 文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

下面,我们对上述状态管理库进行逐一对比介绍。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Class 时代

Redux

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

单向数据流

他的工作流程大致如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • 用户在view层触发某个事件,通过dispatch发送了action和payload
  • action和payload被传入reducer函数,返回一个新的state
  • store拿到reducer返回的state并做更新,同时通知view层进行re-render

前端开发:什么是状态管理及React Context 文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

由此可看出 Redux 遵循“单向数据流”和“不可变状态模型”的设计思想。这使得 Redux 的状态变化是可预测、可调式的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

三大原则

此外,Redux 还遵循三大原则:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • 单一数据源

整个应用的 全局 state被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store中。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • state 是只读的

唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • 纯函数修改

通过 reducer 修改状态,reducer 是纯函数,它接收之前的 state 和 action,并返回新的 state。记住,一定要返回一个新的对象,而不是修改之前的 state。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

如何处理异步

redux没有规定如何处理异步数据流,最原始的方式就是使用Action Creators,也就是在制造action之前进行各种的异步操作,你可以把要复用的操作抽离出来。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

当然这样并不优雅,在实际项目中我们通常使用类似redux-thunk、redux-saga这些中间件来支持处理异步。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

如何处理数据间联动

react-redux的useSelector获取状态后,你可以编写一些逻辑来处理派生状态。如果派生状态需要复用,记得给抽离出来。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

优点

  • 繁荣的社区,像不支持异步这种问题是由成熟的中间件可以解决的,你遇到的问题多多少少可以在社区找到答案;
  • 可扩展性高,中间件模式让你可以随心所欲的武装你的dispatch;
  • 单一数据源且是树形结构,这让redux支持回溯,在调试上也更方便;
  • 有成熟的开发调试工具 redux devtools。

缺点

  • 陡峭的学习曲线。将副作用扔给中间件来处理,导致社区一堆中间件,学习成本陡然增加。比如处理异步请求的 Redux-saga、计算衍生状态的 reselect;
  • 大量的模版代码。使用 redux,开发者要编写大量和业务逻辑无关的模板代码,这给开发和后期维护都带来了额外的成本;
  • 大状态量情况下,性能较差。state 更新会影响所有组件。每个 action 都会调用所有 reducer;
  • reducer 要返回新的对象,如果更新的值层级较深,更新成本也很高;
  • 更多的内存占用,由于采用单一数据源,所有状态存储在一个 state 中,当某些状态不再需要使用时,也不会被垃圾回收释放内存;

当然,redux 也在致力于解决上述缺点。比如,redux toolkit就旨在让开发者使用标准方式编写 redux 逻辑。主要解决 redux 的 3 个问题:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • 配置 redux store 过于麻烦;
  • 必须手动额外添加很多包才能正常使用 redux;
  • redux 需要太多模板代码。

不过,即使有 redux toolkit 的加持,redux 的学习成本依旧不低。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Dva

dva 首先是一个基于 redux和 redux-saga的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router和 fetch,所以也可以理解为一个轻量级的应用框架。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Dva 的特点:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • 易学易用,仅有 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 开始就没有新的版本发布,看起来已经处于不维护状态。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

icestore

icestore 是 IceJs内置状态管理库。icestore 是面向 React 应用的、简单友好的状态管理方案。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

它包含以下核心特征:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • 简单、熟悉的 API:不需要额外的学习成本,只需要了解 React Hooks,对 Redux 用户友好。
  • 集成异步处理:记录异步操作时的执行状态,简化视图中对于等待或错误的处理逻辑。
  • 支持组件 Class 写法:友好的兼容策略可以让老项目享受轻量状态管理的乐趣。
  • 良好的 TypeScript 支持:提供完整的 TypeScript 类型定义,在 VS Code 中能获得完整的类型检查和推断。

icestore 的灵感来自于 rematch和 constate。整体实现和 rematch 基本一致。rematch 是一个没有模板代码的 redux 最佳实践。icestore 整体配置简单,解决了 redux 学习成本高、大量模板代码等问题,同时又很好的支持了异步处理、TypeScript 和 SSR。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

IceJS 自己给出的能力对照表:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • O: 支持
  • X: 不支持
  • +: 需要额外地进行能力扩展

前端开发:什么是状态管理及React Context 文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Mobx

设计思想

MobX 的主要思想是用「函数响应式编程」和「可变状态模型」使得状态管理变得简单和可扩展。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

MobX背后的哲学很简单:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

任何源自应用状态的东西都应该自动地获得。其中包括UI、数据序列化、服务器通讯,等等。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

React 和 MobX 是一对强力组合。React 通过提供机制把应用状态转换为可渲染组件树并对其进行渲染。而MobX提供机制来存储和更新应用状态供 React 使用。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

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

心智模型

Mobx的心智模型和react很像,它区分了应用程序的三个概念:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • State(状态)
  • Actions(动作)
  • Derivations(派生)

首先创建可观察的状态(Observable State),通过Action更新State,然后自动更新所有的派生(Derivations)。派生包括Computed value(类似useMemo或useSelector)、副作用函数(类似useEffect)和UI(render)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

前端开发:什么是状态管理及React Context 文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Mobx虽然心智模型像 react,但是实现却是完完全全的 vue:mutable + proxy(为了兼容性,proxy实际上使用Object.defineProperty实现)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

使用反react的数据流模式,注定会有成本:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • Mobx的响应式脱离了react自身的生命周期,就不得不显式声明其派生的作用时机和范围。比如副作用触发需要在useEffect里再跑一个autorun/reaction,要给DOM render包一层useObserver/Observer,都加大了开发成本。
  • Mobx会在组件挂载时收集依赖,和state建立联系,这个方式在即将到来的react 18的并发模式(Concurrent Mode)中,可能无法平滑地迁移。为此,react专门开发了create-subscription方法用于在组件中订阅外部源,但是实际的应用效果还未可知。

尤大本人也盖过章:React + MobX 本质上就是一个更繁琐的Vue。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Mobx vs Redux

Mobx和Redux的对比,实际上可以归结为 面向对象 vs 函数式和 Mutable vs Immutable。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • 相比于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 特性。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Hooks 的引入主要是为了解决 React Class 组件的以下问题:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

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

Class 组件会将视图和状态逻辑糅杂在一起,如果想复用组件中的状态逻辑,需要使用 render props 和高阶组件,但是这类方案需要重新组织组件结构,会形成组件的嵌套地狱,代码逻辑也会变得难以理解。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

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

Class 组件的状态逻辑会充斥在各个生命周期中,完全不相关的代码出现在同一个生命周期函数中,逻辑难以理解,容易引发 bug,且在多数情况下,很难将组件拆分成更小的粒度。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Hooks 是一种开发理念和组织理念的革新,有 3 个特性:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • primitive。元数据化,将混沌的 state 打散为一个个元数据;
  • decentralization。去中心化,Class 时代的理念是“顶层下发”,Hooks 带来了强烈的“组件自治”理念;
  • algebraic effects。代数效应,剥离组件中的副作用,让开发者更专注业务逻辑。

代数效应是函数式编程中的一个概念,用于将副作用从函数调用中分离。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

自下而上模式的崛起

我们可以看到以前的状态管理解决方案,如Redux,设计理念是状态 「自上而下」流动。它「倾向于在组件树的顶端吸走所有的状态」。状态被维护在组件树的高处,下面的组件通过选择器拉取他们需要的状态。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

在新的组件构建理念中,一种「自下而上」的观点对构建具有组合模式的应用具有很好的指导作用。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

而hook就是这种理念的践行者,即把可组合的部件放在一起形成一个更大的整体。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

通过 hook,我们可以从具有巨大全局存储的「单体状态管理」转变为向自下而上的 「微状态管理」,通过hook消费更小的状态片。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

像接下来要介绍的 Recoil 和 Jotai 这样的流行库以其 「原子状态」的概念体现了这种自下而上的理念。「原子是一个最小但完整的状态单位」。它们是小块的状态,可以连接在一起形成新的衍生状态。最终形成了一个应用状态图。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

这个模型允许你自下而上地建立起「状态图」。并通过仅使图中已更新的原子失效来优化渲染。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

这与拥有一个大的单体状态球形成鲜明对比,你可以「订阅并试图避免不必要的渲染」。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

接下来我们要介绍 5 个 Hooks 时代的状态库,分别是 recoil、zustand、jotai、valtio、hox。比较有趣的是其中 3 个都是 Daishi Kato开发的,采用了不同的设计思想,但是都在短期内取得不错的社区热度,这 3 个库分别是 zustand、jotai、valtio,这三个词其实是“状态”在 3 种语言中的不同发音。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

zustand 🇩🇪 德语 "状态",jotai 🇯🇵 日语 "状态"、valtio 🇫🇮 芬兰语 "状态"。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Recoil

简介

Recoil 是在React Europe 2020 Conference上 facebook 官方推出的专为 react 打造的状态管理库,动机是解决react 状态共享模式的局限性:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • 以往只能将state提升到公共祖先来实现状态共享,并且一旦这么做了,基本就无法将组件树的顶层(state 必须存在的地方)与叶子组件 (使用 state 的地方) 进行代码分割
  • Context 只能存储单一值,无法存储多个各自拥有消费者的值的集合

设计思想

Recoil的状态集是一个有向图 (directed graph),正交且天然连结于React组件树。状态的变化从该图的顶点(atom)开始,流经纯函数 (selector) 再传入组件。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

前端开发:什么是状态管理及React Context 文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Recoil 定义了一个有向图 (directed graph),正交同时又天然连结于你的 React 树上。状态的变化从该图的顶点(我们称之为 atom)开始,流经纯函数 (我们称之为 selector) 再传入组件。基于这样的实现:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • 我们可以定义无需模板代码的 API,共享的状态拥有与 React 本地 state 一样简单的 get/set 接口 (当然如果需要,也可以使用 reducer 等进行封装);
  • 我们有了与 Concurrent 模式及其他 React 新特性兼容的可能性;
  • 状态的定义是渐进式和分布式的,这使代码分割成为可能;
  • 无需修改对应的组件,就能将它们本地的 state 用派生数据替换;
  • 无需修改对应的组件,就能将派生数据在同步与异步间切换;
  • 我们能将导航视为头等概念,甚至可以将状态的转变编码进链接中;
  • 可以很轻松地以可回溯的方式持久化整个应用的状态,持久化的状态不会因为应用的改变而丢失。

正交:相互独立,相互间不可替代,并且可以组合起来实现其它功能文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Recoil每一次状态变更都会生成一个不可变的快照,利用这个特性,可以快速实现应用导航相关的功能,例如状态回溯、跳转等。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

核心方法

Recoil中定义状态的两个核心方法:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • atom: 定义原子状态,即组件的某个状态最小集,
  • selector: 定义派生状态,其实就是computed value

消费状态的方法有 useRecoilState、useRecoilValue、useSetRecoilState 等,用法和 react 的 useState 类似,几乎没有上手成本。另外值得注意的是,recoil目前只支持FC的hook用法,Class组件想用的话可以通过HOC的方式获取状态并注入组件。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

优点

  • 状态原子化(atom),自由组合和订阅;并且状态定义是渐进式和分布式的,使代码分割成为可能;
  • 没有模板代码,天然是 hook 模式,让 react 尽量保持原来的样子;
  • 兼容并发模式(Concurrent Mode);
  • 提供对状态流的快照(snapshot)支持,可以轻松回溯应用状态,甚至将snopshot编码放进url,让任何人打开应用都能进入到同样的状态;

能力对照表

前端开发:什么是状态管理及React Context 文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Zustand

简介

zustand 是一个轻量级状态管理库,和 redux 一样都是基于不可变状态模型和单向数据流的,状态对象 state 不可被修改,只能被替换。渲染优化要手动通过 selectors 进行。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Zustand vs Redux

  • zustand 和 redux 是非常像的,都基于不可变状态模型,都基于单向数据流。
  • 不过,redux 需要应用被 Context Provider 包裹,zustand 则不需要。
  • 二者更新数据的方式不同,redux 基于 reducers,更新状态的 reducers 是严格的方法,这就使得状态更加可预测。zustand 不使用 reducers 而是通过更灵活的方法来更新状态。

特点

zustand 的特点:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • 轻量级;
  • 中心化,单一 store;
  • 不可变状态模型;
  • 不固执。很少限制,非常开放。

Jotai

简介

jotai 是一个小型全局状态管理库,它模仿了 useState、useReducer。jotai 有个叫做 atom 的概念,用于表示小的状态片段。和 zustand 不同的是,他是一个组件级别的状态管理库。和 zustand 相同的是同样都基于不可变状态模型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

jotai 是 Context 和订阅机制的结合,是面向 React 的一种全局状态管理库。如果你的需求是一个没有额外重复渲染的 Context,那 jotai 是个不错的选择。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

特点

jotai 有两个特点:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • 语法简单
  • jotai 的状态不是全局状态

atom 可以在 React 组件的生命周期里创建和销毁。这通过多个 Context 是无法实现的,因为使用 Context 增加一个新的 state 意味着增加一个新的 Provider 组件,如果新增一个组件,它所有的子组件都会被重新挂载,会失去所有状态。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • 衍生 atom

atom 可以像积木一样被组合,生成新的 atom,从而实现复杂逻辑。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

jotai 通过 atom 之间的依赖来实现自动渲染优化。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

推荐场景:组件为中心的应用。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Recoil vs Jotai

jotai 深受 recoil 启发,设计理念基本相同。但有以下不同:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • 最大的不同是是否需要键字符串,开发 jotai 的一大动力就是要省略键字符串。因为键属性必须是唯一的,键命名是一项艰巨的任务;
  • 另一个不同是 jotai 不需要使用 Provider 包裹组件,这对开发者来说可以大幅降低开发成本和心理负担。

Zustand vs Jotai

Zustand 和 Jotai 之间有两个主要不同:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • Zustand 是单一 store,Jotai 由原子 atom 组合而成;
  • Zustand 的 store 存储在 React 外部,Jotai 的 store 存储在 React 内部。

Valtio

简介

基于可变状态模型,利用 Proxy 获取一个和 React 集成在一起的不可变快照。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

利用 Proxy 自动进行重新渲染优化,这个过程使用了状态使用跟踪技术。通过状态使用跟踪,可以检测到状态的哪部分被使用,让组件实现按使用重新渲染。同时,开发者也可以编写更少的代码。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Valtio vs Zustand

zustand 基于不可变状态模型,valtio 基于可变状态模型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

valtio 通过属性访问自动进行渲染优化,zustand 推荐使用 selectors 手动进行渲染优化。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Valtio vs Mobx

渲染优化上,valtio 使用 hook,mobx 使用高阶组件。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Hox

从状态管理到状态共享

redux、zustand、recoil 这些状态管理库,它们虽然在一定程度上也可以帮我们解决数据共享的问题,但它们最本质的能力还是对数据的操作。它们被称做也确实应该被称做"状态管理"工具。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

而 Hox 想解决的问题,不是如何组织和操作数据,不是数据流的分层、异步、细粒度,我们希望 Hox 只聚焦于一个痛点:在多个组件间共享状态。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

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

优势

  • 直接复用已有的 React 知识,几乎没有学习成本,你怎么写 React 组件,就可以怎么写 Store
  • 为灵活重构而设计,在 Store 和组件中使用同一套 DSL 可以让你几乎 0 成本的将组件的局部状态转化为一个组件间共享的状态
  • 同时支持局部状态和全局状态,在灵活和简单之间做到了很好的平衡
  • 优秀的性能和 TypeScript 支持

总结

简单场景使用原生的useState、useReducer、useContext就能满足;还可以用 Hox这样小而美的库将hook的状态直接拓展成持久化状态,几乎没有额外的心智负担。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

复杂场景的应用,redux、mobx都是经受过千锤百炼的库,社区生态也很完备。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Redux高度模板化、分层化,职责划分清晰,塑造了其状态在可回溯、可维护性方面的优势;搭配thunk、saga这些中间件几乎是无所不能。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

Mobx的优势是写法简单和高性能,但状态的可维护性不如redux,在并发模式中的兼容性也有待观察。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

随着hook和有官方背景的recoil的出现,状态管理似乎在朝原子化、组件化的方向发展,这也符合react的组件化哲学。Redux的暴力遍历和分发或许已经是逆潮流的解法。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

没有最好的状态管理库,只有最合适的状态管理库。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

详细状态库能力对照表:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

前端开发:什么是状态管理及React Context 文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

参考资料文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html

  • 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
文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/50489.html
  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/gcs/50489.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定