2023 年末 React 状态管理现状

在我的认知中, React 状态管理库可以分为三类:

  • 基于事件派发 : 需要调度 Action 来更新状态,通常称为"单一数据源"。在这个组中,我们有Redux Zustand
  • 基于原子化 : 将状态分割成称为原子的微小数据块,可以使用 React hooks 对其进行写入和读取。在这个小组中,我们有RecoilJotai
  • 基于可变数据 : 通过代理/劫持操作来创建可变数据源,该数据源实现了直接写入或被动读取的逻辑。该组的候选者是 MobXValtio

现在我们已经介绍了 React 状态管理库的三个主要类别。让我们更深入地研究每种方法并探讨每种方法的优点和缺点。这将帮助您了解哪个库最适合您的项目需求:

基于事件派发

尽管人们普遍批评 Redux 过于复杂,但它自创建以来一直是最受欢迎的状态管理库。

优势:

  • 强大的状态机和时间机。假设应用程序所有状态都集中位于State状态内(这种情况很少发生,因为组件中可能有本地状态),则存在以下公式:UI = React(State)。这意味着单个状态值只会产生一个 UI,因此您的应用程序在特定状态下看起来始终相同。如果您在某处备份整个状态,然后发送类似 REVERT(pastState) { state = pastState } 的更改,您的 UI 将被恢复,就像它是捕获的屏幕截图一样。

  • 最好的 DevTools 支持:通过使用显式操作更新状态,DevTools 可以帮助您指出状态更改的内容、时间和方式。你可以想象它就像在你的应用程序状态中拥有一个 Git 提交历史记录,这有多酷?

劣势:

  • 代码冗余:即使对状态进行简单的更改也需要对代码进行大量更改。

  • 陡峭的学习曲线:虽然其核心很简单,但仅靠它自己是远远不够的。要真正掌握 Redux,您应该知道如何将它与其他库(例如 Saga、Thunk、Reselect、Immer 或 Redux Toolkit)一起使用。老实说,当我大部分时间在 Saga 中编写生成器时,我只是想通过网络获取一些数据,这感觉有点矫枉过正。我们现代 JS 开发人员倾向于每天使用 async/await。

    备注: (Saga、Thunk 都用于帮助开发者更方便地处理异步数据,具体细节可查看 juejin.cn/post/710530...)

  • TypeScript:虽然完全支持 TypeScript,但需要显式地定义好数据类型,这将耗费开发者大量时间。而其他方法直接支持自动类型推断。

基于原子化

这种方法不是将整个应用程序状态放入一个集中式State中,而是将其拆分为多个原子,每个原子最好像原始类型或基本数据结构(如同基本数据类型与引用数据类型之间的关系一样)一样小。然后,如果需要,您可以稍后使用选择器将相关状态分组在一起。

优势:

  • 利用 React 功能:这是预期的,因为 Recoil 和 React 都是由 Facebook 创建的。 Recoil 与 Suspense、Transition API 和 Hooks 等尖端 React 功能配合得很好。
  • 简单且可扩展:通过仅使用原子和选择器,您仍然可以有效地构建巨大的反应式应用程序状态,同时对各个状态更改进行细粒度控制。现在,提升状态就像声明一个原子并将 useState 挂钩更改为 useRecoilState 一样简单。
  • TypeScript:作为一名关心 DX(开发体验) 的开发人员,就像用户关心 UI(用户界面) 和 UX(用户体验) 一样,我发现 React、Recoil 和 TypeScript 是一个美妙的组合。在我的项目中,大多数时候类型都是自动推断的。

劣势:

  • 没有配套的 Devtools.
  • 无法在组件之外使用状态.

基于可变数据

提示:"可变"和"不可变"是指数据创建后的更改行为。

person.age += 1 // 可变`

person = { ...person, age: person.age + 1 } // 不可变

优势:

  • 最简单的 API:通过允许直接改变状态,组件和状态之间不需要额外的代码,除非您愿意这样做。
  • 反应性和灵活性:只要状态发生变化,依赖关系就会自动更新。这简化了您的应用程序逻辑并使其更易于理解。此外,基于代理的方法有助于最大限度地减少不必要的重新渲染。这也意味着流畅的性能和更灵敏的用户体验。

劣势:

  • 魔法太多:自动反应(即基于可变数据的更新行为)是一把双刃剑。异步更新中的竞争条件可能会导致应用程序状态混乱,并且在复杂的应用程序中调试更改流程可能具有挑战性。
  • DevTools:没有类似 Dedux 一样的 Devtools.
  • 不一致的 DX(开发者体验):React 本身的更新机制是基于"不可变"数据更新,在我的项目中混合"可变"数据更新行为有时会让我对更改数据感到不安全。

最佳选择

同样,最适合您的项目的 React 状态管理库取决于您和您的团队的特定需求和专业知识。请不要:

  • 仅根据项目规模和复杂性选择库。因为,您可能在某处听说过 X 更适合大型项目,而 Y 更适合小型项目。库作者在设计他们的库时考虑到了可扩展性,而项目的可扩展性取决于您编写代码和使用库的方式,而不是您选择使用哪些库。

  • 将您从一个 library 学到的最佳实践应用到另一个 library。将整个应用程序状态放入单个 Recoil 原子中以实现"单一事实来源"只会导致状态更新和性能问题。就像在 Redux 中定义 action 一样,在 set 过程中 dispatch 多个 action,而不是在一次提交中批量更改。

作者的选择 - jotai

我个人更喜欢原子库,因为上面列出的优点以及我在处理异步数据获取(其实 zustand 不会有这个问题)和使用 批量加载 UI (这一块确实是原子化的优势) 时的无痛 DX(开发者体验)。 Jotai比Recoil做得更好的是:

不需要key。命名这件事很困难,而且大多数时候,您不会使用 Recoil 的 key 。那么,当库可以自动为您提供key 时,为什么还要花时间声明它们呢?这是 Recoil 的回答;然而,正如你所看到的,人们并不十分认可。

看看性能表现。一图胜千字,而我,有4个:

Recoil

  • 包体积:
  • LCP:

Jotai

  • 包体积:
  • LCP:

您可能会说,大约 20Kb 的大小差异并不重要,但让我们看一下在非常旧的 Android 设备上进行的基准测试,其中缓慢现象与填充对角红色条纹图案的条形一样明显。正如您所看到的,Jotai 内部逻辑需要较少的整体计算,这将我的应用程序的 LCP(一项重要的 Core Web Vitals 指标)从约 2.6 秒提高到约 1.2 秒。尽管如此,这种比较可能没有考虑到 Recoil 比 Jotai 做得更好的其他因素(事实上,我的知识仅限于此)。我只想说Jotai团队在那里做得非常出色。

我希望这对你有帮助!

相关文章推荐:

# 【前端状态管理】React 状态管理工具如何选 context/redux/mobx/zustand/jotai/recoil/valtio

相关推荐
慧一居士21 分钟前
flex 布局完整功能介绍和示例演示
前端
DoraBigHead23 分钟前
小哆啦解题记——两数失踪事件
前端·算法·面试
一斤代码6 小时前
vue3 下载图片(标签内容可转图)
前端·javascript·vue
中微子6 小时前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
光影少年6 小时前
从前端转go开发的学习路线
前端·学习·golang
中微子6 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
3Katrina6 小时前
深入理解 useLayoutEffect:解决 UI "闪烁"问题的利器
前端·javascript·面试
前端_学习之路7 小时前
React--Fiber 架构
前端·react.js·架构
伍哥的传说8 小时前
React 实现五子棋人机对战小游戏
前端·javascript·react.js·前端框架·node.js·ecmascript·js
qq_424409198 小时前
uniapp的app项目,某个页面长时间无操作,返回首页
前端·vue.js·uni-app