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

相关推荐
优雅永不过时·22 分钟前
Three.js 原生 实现 react-three-fiber drei 的 磨砂反射的效果
前端·javascript·react.js·webgl·threejs·three
神夜大侠3 小时前
VUE 实现公告无缝循环滚动
前端·javascript·vue.js
明辉光焱3 小时前
【Electron】Electron Forge如何支持Element plus?
前端·javascript·vue.js·electron·node.js
柯南二号3 小时前
HarmonyOS ArkTS 下拉列表组件
前端·javascript·数据库·harmonyos·arkts
wyy72933 小时前
v-html 富文本中图片使用element-ui image-viewer组件实现预览,并且阻止滚动条
前端·ui·html
前端郭德纲4 小时前
ES6的Iterator 和 for...of 循环
前端·ecmascript·es6
王解4 小时前
【模块化大作战】Webpack如何搞定CommonJS与ES6混战(3)
前端·webpack·es6
欲游山河十万里4 小时前
(02)ES6教程——Map、Set、Reflect、Proxy、字符串、数值、对象、数组、函数
前端·ecmascript·es6
明辉光焱4 小时前
【ES6】ES6中,如何实现桥接模式?
前端·javascript·es6·桥接模式
PyAIGCMaster4 小时前
python环境中,敏感数据的存储与读取问题解决方案
服务器·前端·python