
当你有机会构建大型前台时,就需要在你的应用中进行全局的状态存储。这可能是为了存储关于登录的用户的数据,维护你的用户界面的特定部分的元数据或来自服务的数据。问题是,你如何才能真正实现这一点?
这篇文章旨在涵盖其中的三个,即Apollo的Local Cache、Recoil和Redux。我将以如下方式介绍这三种工具。概述,代码演练,思考。
每个工具都会被打分,以衡量如果我在React中构建一个大型的前端Web应用,我是否会将其加入我的前端堆栈。
所有这些实验都是从 React Bleeding Edge工具包中产生的,这是一个包含Apollo Client、TailwindCSS和Reach Router的电池工具包,在Create React App (CRA)的基础上增加了精英级生产力。
阿波罗本地缓存

概述 📡
如果你在React或[Vue](http://Vue Apolloapollo.vuejs.org)中使用Apollo客户端,你可以使用它的本地缓存进行状态管理。更重要的是,你可以使用GraphQL来查询和改变你的数据。在下面的演练中,我使用了Apollo 2,它需要使用本地解析器,但是请记住,它们已经被淘汰了。
你可能想知道为什么这个作者的小丑要带你学习将被废弃的东西? 简而言之,我不喜欢Apollo的状态管理论文,但我的偏见在后面🥲。不过,一定要看看Apollo 3的最新实现,你也可以看看Laura Beatris关于它的演讲🍦。
代码演练 🖥️
要在React应用中设置Apollo的缓存,在index.js文件中,你首先要做的是初始化你想保持记录的状态。
通过使用cache.writeDatafunction,全局状态被存储在应用程序的缓存中。你完全有必要添加__typenamefield,这样Apollo就知道你要查询和改变对象的哪一部分。如果我需要在额外的状态对象上存储更多的数据,我将需要创建它们并为每个字段指定那个__typename字段,这已经是一个巨大的缺点了。
接下来我不得不创建一个本地Resolvers文件,其突变是这样的。
我正在读取缓存中的最新版本的表单数据。如果queryResult不是从中未定义的,我就可以通过从突变中读取参数并将其写入缓存来继续创建一个新的formData对象。
为了从缓存中读取数据,我们首先需要写一个查询来实现。
这个模式文件基本上是在客户端查询formData对象的电子邮件、客户和日期。你将需要在整个应用程序的其他组件中再次使用它。
接下来,我们需要创建一个突变,使我们能够更新formData。
它接受电子邮件、日期和客户输入。让我们看看它的操作,我们有一个表单,允许人们添加一个数组的客户对象。
这个组件允许我们将多个客户添加到一个数组中,为了将数据存储到本地,我们首先运行查询来获得客户、电子邮件和日期,同时我们运行突变来更新将要来旅行的客户。这样,当我们把用户带到下一个组件时,我们就有了商店的最新版本。
思考 🤔
撇开本地解析器不谈,整个设置是不可预测的,当你最终可能在本地状态下存储一个有超过15个字段的对象时,添加GraphQL的模式验证就不会有什么效果了。Apollo 3旨在解决这个问题,但实施起来似乎并不直接,因为如果你有一个大的应用程序,你将需要把你的状态管理逻辑放在不同的文件中,以便它是模块化的。因此,作为一个GraphQL的拥趸,我很伤心地说,我没有看到使用它的价值。
**评价:**⭐
回溯

概况 📡
Recoil是在2020年中期发布的,目的是缓解与使用React的内置状态管理工具相关的一些问题(至少从我的角度看是这样的🦧)。
它的核心概念是围绕着形成状态片段的Atoms和允许你改变状态的选择器。选择器利用对象中的嵌套函数,使你能够使用钩子将状态读入组件,并改变状态。它的目的是使状态管理的过程变得简单,不那么像模板。
代码演练 🖥️
这个演示以一级方程式赛车(F1)为主题。我甚至为我们使用了公共的F1 API来进行一些异步调用。我有一个叫atoms.js的文件,里面有我想追踪的所有状态。
你需要初始化状态的方式是非常直接的,以带键的对象的形式,这一点非常重要,因为这是你在应用的不同部分引用这个状态的方式。此外,还有一个默认键,通过指定其类型(对象、字符串、数组等)来添加默认状态。在我的例子中,我正在追踪driverState,它是一个数组,circuitState也是一个电路数组,singleDriverState在一个对象中存储所有关于一个驱动的信息。
我觉得只要在索引页上显示所有的驱动程序,同时从状态中读取它,就能快速获胜。
有了useRecoilValue()钩子,你可以很容易地引用你初始化为原子的状态,把它作为一个参数传入钩子,就像我对driverState做的那样。
接下来我们有一个异步的例子,这是许多应用程序的一个共同要求。想象一下,你想获得一个用户当月的银行交易,并允许应用程序的不同部分访问它。你可以像这样在你的选择器中使用selectorFamily()util。
在我的案例中,我想获得一个司机的信息,但要做到这一点,我需要他们的名字。key属性是让这个选择器意识到我们正在处理驱动程序的原子。而get()键允许我们实际调用一个异步函数来获取一个驱动程序的细节。set键是一个函数,现在允许我们将关于驱动程序的数据存储到驱动程序原子中。
思考 🤔
总的来说,Recoil很好,很灵活。当然,这是一个基于钩子的现代React库,它有一个很好的干净的API和文档,使它很容易实现。它不能得到五分的唯一原因是我不得不过度加热我的大脑来彻底仔细思考选择器。🎭
评价:⭐⭐⭐⭐
重述

概述
最后但并非最不重要的是,我们有反应社区的宠儿,Redux!Redux最初是为了防止prop-drilling的问题。Redux的工作模式是通量模式,即设置一个初始状态,该状态与一组帮助应用程序减少其状态的动作相关联。有不同的方法来写出Redux应用程序,可以是_锅炉_式的,幸运的是我在掌握了如何用文件和函数以模块的方式使用Redux后发现了Redux工具包。
代码演练
我通常这样构建我的Redux应用程序。

相当臃肿,你需要在几个文件中进行修改才能看到任何结果。这样做会适得其反,是一种不必要的脑力劳动。
幸运的是,有一个叫做Redux工具包的东西,它有点采用了与Recoil相同的架构原则。你只需要做一个分片,它可以被称为一块状态。在这个案例中,我做了一个布局分片,以便能够改变每条路线的页面标题。
layout是我用来跟踪pageHeader字符串的状态名称。它被赋予了一个默认值为 页面。 我有一个叫setPageHeader的还原器来设置pageHeader的值,更多细节请看刚才如何将还原器导出到它们的文件中。
现在来看看减速器!在一个名为layoutReducer的文件中。
我简单地导出了一个名为setNewTitle的函数,它将有效载荷分配回全局状态中的pageHeader。
我们现在可以用useSelector钩子在全局App组件中消费这个状态。
改变pageHeader标题的一种方法是使用useEffect钩子。
通过使用useDispatch钩子,我们能够使用我们在dispatch函数中导出的setPageHeader动作。这比创建mapDispatchToProps和mapStateToProps函数整洁多了。
同样地,我在状态中初始化了一个山的数组,并能够像这样通过应用程序中的一个表单来调度它们。
用一个看起来像这样的reducer。
思考 🤔
与我在知道Redux工具包之前写Redux的方式相比,这很好,很干净。随着应用的发展,它可以很容易地被抽象出来。
评价: ⭐⭐⭐⭐
总的来说,我是一个知道如何正确架构前端的大粉丝,因为如果状态管理设置得不好,会很麻烦。Recoil和Redux工具包肯定会使人们能够确保状态管理层是简洁和模块化的。
作为总结,这里有各种Repos的链接🔌。
Apollo
回收
ǞǞǞ
普通版Redux:链接到版本库
鸣谢 🦠
感谢 本-贾内克 的帮助下阅读了这个草稿。 🧙♂️
2021年React状态管理技术概述 ⚜️🌐最初发表在ITNEXT的Medium上,人们通过强调和回应这个故事继续对话。