状态管理工具
状态管理工具
(可选课程)
这是我被问到的最常见的 React 问题之一:
我们应该使用像 Redux 这样的工具来管理全局状态吗?还是 React 本身就足够强大?
令人沮丧的是,我对这些问题没有简单的“是”或“否”答案。不幸的答案是,这要看情况!
在本课中,我们将深入探讨这些问题。我们将探索一些关于 Redux 的历史,了解它为何有用,以及今天的现状。我还会分享我在决定如何管理应用程序状态时使用的公式。
一些背景信息
在 React 的早期,人们预计 React 只会是你前端技术栈中的一部分。最佳实践是将 React 与 Flux、Redux 或 MobX 等结合使用。
具体来说,想法是使用 React 处理本地状态,而使用像 Redux 这样的工具处理全局状态。
“局部状态”是指仅在应用程序的某个特定部分需要的状态。我们在本模块中看到的大多数示例都是局部状态。比如你点击了多少次两元硬币,或者受控文本输入的当前值是多少。
“全局状态”则用于更广泛的内容。例如,关于当前登录用户的数据。或者当前选择的颜色主题(深色/浅色)。一条全局状态可能在应用程序的十几个不同地方被使用。
(这两类之间的界限可能模糊;这更像是一个光谱,而不是二元选择。)
这种想法是,React 状态非常适合小型、简单的本地状态,但对于全局状态来说,它的功能不够强大或灵活。跨应用程序移动状态变量太困难,而且很难协调复杂的状态变化。
从 2014 年到 2016 年,许多包竞争成为 React 的“全局状态”合作伙伴。最终,毫无争议的冠军是 Redux。
Redux 简介
很难在不跑题的情况下描述 Redux,但我会尽力快速总结一下。
在 Redux 中,我们的全局状态被表示为一个浮动在 React 应用外部的单一对象。此状态无法直接编辑;相反,Redux 侦听“动作”,这些事件表示我们的应用中发生了某些事情。
所有操作都被 Redux 仔细记录,可以像阅读故事一样了解我们应用程序中的情况。例如,一个电子商务应用的日志可能看起来像这样:
- 用户登录
- 用户提交搜索表单
- 从服务器接收到的搜索结果
- 用户点击搜索结果的“第 2 页”
- 从服务器接收到的搜索结果
- 用户将“Hello Kitty Coffee Machine”商品添加到购物车
这些操作都通过 Redux 触发,我们可以编写一些代码来控制这些操作如何影响状态。例如,以下是我们如何处理最后一个“将商品添加到购物车”操作的:
在 Redux 中,状态更新是不可变的,和 React 一样。事实上,Redux 状态与 React 状态有很多相似之处!但 Redux 给了我们额外的超能力。
例如,Redux 开发工具具有一个称为“时间旅行调试”的功能。我们可以逐步查看 Redux 详细的操作日志,并在每个时刻检查用户界面,进行倒带和暂停。Redux 是调试时的终极侦探工具。
Redux 是非常可定制的。它提供了一系列额外的 API,例如中间件和 store enhancers,以扩展 Redux 的开箱即用功能。因此,出现了丰富的 Redux 插件生态系统,这些工具使 Redux 更加强大。
但所有这些强大和灵活性都是有代价的:使用 Redux 时会有相当大的摩擦。所有事情都需要花费更多时间,并且需要更多代码,因为你必须搭建一堆东西。关于 Redux 的头号抱怨是“样板代码太多”。
它还有一个相当陡峭的学习曲线。Redux 在功能编程原理上依赖很重,这需要一些时间来理解。
现代 React 中的相关性
所以,几年前将 Redux 用于管理全局状态被认为是“最佳实践”。但是,在现代基于 hooks 的 React 时代,情况如何呢?Redux 仍然相关吗?
这是一个有争议的问题,也是社区中仍然经常讨论的内容。没有一个被广泛接受的答案。
有人说 Redux 仍然非常有用;它提供了大量复杂的工具和见解。他们会指出 NPM 下载量,这表明 Redux 仍在不断增长。
另一方面,人们会指出,React 在过去几年中发展了很多。它变得更加自主。它甚至集成了一些 Redux 的功能,比如 useReducer 钩子!现代 React 提供了大部分好处,且没有缺点。
有趣的是,Redux 是由 Dan Abramov 和 Andrew Clark 创建的。他们现在都在 React 核心团队工作,并已与 Redux 保持距离。以下是 Dan 关于他何时会选择使用 Redux 的看法:
我认为:两个小组都有一些好的观点,我认为他们在某些情况下都是正确的。但这真的取决于我们正在构建的应用程序类型。
我自身的偏见
我将详细阐述我的观点,但首先我想分享一下我与 Redux 的个人历史。
我在 Redux 上是一个“早期采用者”。当它刚推出时,我就在副项目中开始使用它,我非常喜欢它。一旦我掌握了它,我觉得它对我来说是如此优雅和令人愉悦。
我在多个工作中使用过 Redux,包括 Breather、Unsplash 和 Khan Academy。我还使用 Redux 构建了许多副项目。
例如:几年前,我创建了 Beatmapper,这是一个用于 VR 节奏游戏“Beat Saber”的 3D 关卡编辑器:
这个项目广泛使用了 Redux,我相信这在保持应用程序可维护性方面帮助很大!
话虽如此,我还有很多其他项目不使用 Redux。比如,这个课程平台根本不使用 Redux!我的博客 joshwcomeau.com 也不使用。
对我来说,是否使用 Redux 确实取决于应用程序的类型。博客、课程平台和 Beat Saber 关卡编辑器都是非常不同类型的项目,不同的工作需要不同的工具。
应用类型
广义上讲,我认为所有网络应用程序可以分为三类:
1. 不太多的状态
第一类包括我们所称的“大多数网站”。静态网站、新闻网站和博客都属于这一类别。
需要明确的是,这些应用程序可能仍然有相当多的本地状态!例如,我的博客有一些非常动态的组件。但就全局状态而言,情况相对简单。
2. 大多数是客户端状态
这个类别主要是“曾经是桌面应用程序的东西”。Photoshop、视频编辑器和文字处理器都属于这个类别。
在这个类别中,有很多复杂的全局状态。我们在浏览器中进行大量的状态操作。我们可能仍然将结果“保存在云端”而不是用户的设备上,但它们本质上是客户端重型应用程序。
3. 主要是服务器状态
在这个最后的类别中,我们有主要与服务器状态交互的网络应用程序。
我认为最清晰的例子是分析仪表板:
我们在数据库中有一堆复杂的数据,我们的应用程序会获取这些数据并将其呈现给用户。这些应用程序本质上是使用户能够读取和操作存储在某个数据库中的数据的接口。
这是一个广泛的类别。大多数 CRUD?应用程序都属于这一类,包括大多数 SaaS 应用程序、社交网络、搜索引擎、电子商务网站等。
好的,既然我们已经定义了这些类别,那么我们来谈谈它们为什么重要。
适合这项工作的正确工具
所以,我认为将应用程序分成这些类别是有价值的,因为挑战是不同的。
对于类别 1 中大多数静态网站,我们实际上不需要担心全局状态。全局状态并不多,而且相对简单。我的博客属于这一类别,尽管有很多挑战,但全局状态并不是其中之一。我专门使用 React 来管理所有状态,它运行得很好。
对于类别 2 中的客户端重型应用程序,Redux 非常棒。我不会说它是必需的——在过去几年中,React 确实变得更强大了——但它仍然是一个非常有用的工具!Redux 帮助保持代码简单,即使功能变得越来越复杂。而且它有很多内置的性能优化。
当我们谈论第 3 类中服务器密集型应用时,事情变得有趣。
这一类别中最大的与state相关的挑战都是与网络有关的:
- 在正确的时间获取正确的数据。
- 通过重新验证数据(再次获取数据)来确保数据不会过时,并根据具体情况定制重新验证。
- 缓存数据,以便多个组件不重复请求。
- 乐观的用户界面更新,以便我们“预测”网络请求将如何解决。
- 分页,按需请求小块数据,让用户根据需要拉取新数据。
事情是这样的:Redux 并没有真正解决这些问题!Redux 是一个状态管理工具,它不涉及网络相关的内容。
在过去的几年里,出现了几种专门用于帮助我们管理这些网络相关挑战的工具。这些工具包括 Apollo、react-query 和 SWR。我们将在模块 3 中进一步了解它们!
如果你正在开发一个视频游戏或音频编辑器,Redux 是很好的选择。但我们大多数人并不是在构建这些类型的应用程序。我猜 95% 的 React 应用程序要么属于类别 1,要么属于类别 3。
因此,这是我在确定使用哪种工具时所使用的思维模型:
- 如果它是一个静态网站,比如我的博客,我单独使用 React(借助于上下文,在模块 4 中讨论过)。
- 如果是像我的节奏光剑关卡编辑器这样的客户端重应用,我使用 Redux 进行全局状态管理。
- 如果这是一个服务器负载较重的应用程序,比如这个课程平台,我会使用 React + 一些东西来帮助处理网络事务。我正在使用 Vercel 的 SWR。
在这个课程平台上,我没有任何不如意。
这是我经过多年的经验和数十个项目后发现最有效的公式,但很多内容确实取决于主观偏好。
但我可以客观地说一点:React 在过去几年中发生了巨大的变化,变得更加强大。你绝对可以在没有其他状态管理库的情况下构建大型复杂的应用程序。你不需要 Redux,也不应该感到有学习它的压力。
如果你是 React 新手,我建议你:花一到两年的时间熟悉使用本课程中介绍的工具构建应用程序。你可以在未来探索 Redux,看看它是否对你有帮助!
老实说,构建应用程序并没有太多“错误”的方式。我们生活在一个丰富的时代,有很多令人惊叹的工具。最重要的是不要因为试图一次性学习所有内容而感到不知所措!
所有这些话说完,还有一个我们应该讨论的细节:Redux Toolkit。
Redux Toolkit 和 RTK-Query
在过去的几年里,Redux 团队一直在努力创建 Redux Toolkit,这是一个有明确观点的工具集,旨在解决使用 Redux 时的一些难点。
该工具包包括一个项目,RTK Query,这是一个或多或少与 react-query / SWR 兼容的 Redux 版本的工具。
我对这个发展感到非常兴奋……但不幸的是,我与 Redux Toolkit 中固有的观点有根本性的分歧😅。我会在下面分享我的一些小意见,供有兴趣的人参考,但重点是我计划继续使用“经典”Redux,而不使用 Redux Toolkit。
如果你已经在使用 Redux Toolkit,我认为 RTK Query 是一个肯定的选择。使用紧密集成的东西更有意义,而不是试图让 Redux 和 SWR 和谐地一起工作。
否则,我很难说你是否应该使用这个组合而不是其他东西。这不是我的首选,但正如我上面所说,现在有很多很好的选择,而这个可能最适合你,这取决于你正在构建的应用类型和你的主观偏好。
我对 Redux Toolkit 的一些小意见
正如我上面提到的,Redux Toolkit 是一套有明确观点的工具,建立在 Redux 之上,它强制执行了一些约定。遗憾的是,这些约定与我个人喜欢使用 Redux 的方式不太一致。
我应该先说,我对 Redux 团队所做的工作非常尊重。你应该对我接下来要说的内容持保留态度:我很可能是个例外。总体而言,社区似乎对 Redux Toolkit 感到满意。
好的,让我解释一下我的问题。