使用 Signia 实现 React 状态管理
写在前面
如果你在最近的过去开发过任何具有相当复杂程度的 React 应用程序,你可能已经了解状态管理如何很快成为一个主要问题。React 提供的原生工具,如 useState
和 useContext
,在尝试实现常见的设计模式时被证明是不够的,比如由多个组件使用和更新的中央共享状态。
Redux
是帮助解决这个问题的最受欢迎的库;它运行了几年,为了克服它存在的小差距,一个伟大的生态系统以Reselect
和Redux-Saga
等库的形式围绕它发展起来。最近,MobX
,Zustand
和Jotai
等其他替代品越来越受欢迎。在本文中,我们将了解 Signia
,这是一个使用信号来解决相同问题的状态管理库。
什么是 Signia?
正如tldraw
团队在公告博客文章中提到的,“Signia
是一个原始库,用于处理细粒度的反应值,称为signals
,使用基于逻辑时钟的新惰性反应模型”。
简单来说,Signia
使用称为signals
的基元进行状态管理,它可以通过执行增量计算来有效地计算计算值。此外,借助为整个事务的回滚提供支持的内部时钟,如果需要,它们可以实现事务的概念。
虽然核心库与框架无关,但 tldraw
团队还发布了一组 React
绑定,这使得将 Signia
集成到 React
应用程序中变得轻而易举。
signals
到底是什么?
在进入 Signia
的功能之前,让我们先了解一下信号的概念是什么。根据官方文件,“signals
是一个随时间变化的值,其变化事件会引发副作用”。换句话说,signals
是一个纯粹的、无功的值,可以观察到变化。然后,信号库负责观察这些变化,通知订阅者,并触发所需的副作用。
从理论上讲,signals
有点类似于RxJS
库提供的可观察量的概念,但有一些根本区别。其中之一是需要创建订阅并传递回调函数来访问可观察量的值。
Signia
核心概念
让我们回顾一下理解 Signia
所必需的一些概念。
Atom 原子
Signia
中的 Atom
表示对应于根状态的signals
,即应用的真实来源。可以读取和更新其值,也可以在此基础上构建以创建计算值。
创建原子
要创建 Atom
,Signia
库提供了 atom
函数:
上面的代码创建一个名为 fruit
的信号,值为Apple
。还将fruit
作为第一个参数传递给 atom
函数,因为它有助于调试目的。
更新原子
为了更新 Atom
,使用 set
函数,如下所示:
与 React setState
函数类似,有一个接受函数作为参数的set
的替代版本。然后,它使用信号的当前值调用该函数并计算更新的值。
计算信号
计算信号来自原子,因此依赖于它们;每当它们所依赖的原子发生变化时,它们的值就会重新计算。
创建computed
信号
您可以使用 computed
函数创建计算信号,如下所示:
更新computed
信号
没有更新计算信号的直接方法。但是,更新其任何根原子都会自动更新计算出的信号:
如上所示,计算信号的值被更新以反映在fruits
的根原子上设置的最新值。
React 绑定 Signia
到目前为止,我们回顾的代码示例是通用的,使用 Signia
核心库。但是,如前所述,tldraw
团队还发布了一组 React
绑定,可以更轻松地将 Signia
集成到React
应用程序中。官方的 React
绑定以两个包的形式提供,即 signia-react
和 signia-react-jsx
。
signia-react
提供了像 useAtom
和useComputed
这样的钩子,它们有助于管理React
组件中的本地状态。signia-react
还提供了track
和 useValue
等实用程序,您可以使用它们为组件提供反应性,但如果使用 signia-react-jsx
库,则不需要。
signia-react-jsx
提供导致所有功能组件变得跟踪和响应的配置选项。它还解包每个信号,因此不需要将信号包装在 useValue
中。现在,创建一个使用Signia
进行状态管理的 React
待办事项列表应用程序。
上手体验 Signia
Signia
对Vite
有开箱即用的支持,所以将使用 Vite 作为打包器。若要创建新的 Vite
项目,请运行以下命令:
当界面出现时,为新项目提供一个名称,选择 React 作为框架,然后选择 TypeScript 作为语言。创建项目时,应看到类似于以下内容的内容:
我们需要在创建项目的目录中工作,在例子中是 todo-list-signia
目录。
设置 Signia
现在,让我们安装特定于Signia
的库:
我们将为组件设置响应式,这样就不需要手动将每个组件包装在 track
函数中。为了进行设置,在新创建的样板 Vite
项目中打开 tsconfig.json
文件,并将以下代码添加到 compilerOptions
对象:
现在,我们可以开始在样板中使用 Signia
。
设置 Chakra UI
我们还安装 Chakra UI
组件库,将使用它来构建 UI
组件,使它们看起来干净有序。要安装 Chakra UI
及其对等依赖项,请运行以下命令:
在 App.tsx
中进行以下更改:
接下来,使用以下命令运行本地开发服务器:
可以看到该应用程序在 localhost
上启动并运行,显示以下内容:
使用 Signia
测试响应式
在创建实际应用程序之前,测试一下是否正确设置了所有内容。将创建一个简单的计数器应用,该应用使用 Signia
进行状态管理。我们将创建一个 useAtom
的局部状态变量,该变量将保存计数的值,并在每次单击按钮时添加一个增量函数:
当我们单击按钮时,可以看到计数器值已正确更新。因此,设置按预期工作:
设计状态
现在可以将简单值存储为 Signia atom
,继续下一步,给待办事项列表应用程序设计状态。要求是存储两个实体,即项列表和列表标题。可以使用 Signia
团队推荐的基于类的设计,并创建两个单独的 Atom 来存储这些实体。该类将如下所示:
请注意,items class
属性是一个包含与各个项目对应的其他对象的对象,这将有助于我们有效地更新状态。不需要遍历项目来寻找的项目,可以在项目上使用 spread
运算符并只更新感兴趣的项目。
还要注意每个待办事项列表项如何具有三个键, id
、 text
和 completed
。需要向这个类添加能够修改此状态的函数,即 addItems
、 markItemAsDone
和 setTitle
:
上面的代码实现UX
所需的所有最小功能。
创建用户界面
对于待办事项列表应用的 UI
,将在顶部显示标题。若要实现重命名列表的功能,只需提供一个 edit
按钮并调用已在状态类中定义的 setTitle
函数。
在 Title
下方,可以将 input box
与button
一起使用,您可以使用它向列表中添加项目。使用 Chakra UI
,标题的代码以及输入框如下所示:
为了掌握 React
组件内部的状态,我们必须实例化 Todo
类。为此,使用 useMemo Hook
创建状态的记忆版本,如下所示:
还需要创建一个本地状态变量,该变量将跟踪在 input
内键入的文本。可以利用 useAtom 来实现此目的:
还需要两个处理程序,一个用于处理 todo
项的添加,另一个用于将其标记为完成:
单击添加按钮时,在实例化的状态类上调用 addItem
方法。选中该复选框后,使用 ID
调用 markItemAsDone
方法。
还剩下一件事。
循环访问待办事项列表并在 UI
中显示它们。为此,将使用List
和 ListItem
组件以及 Object.values
帮助程序来迭代对象值:
这样就完成了最小待办事项列表应用正常工作所需的所有代码更改。您可以检查完整的代码更改集,甚至可以通过克隆此 GitHub
(https://github.com/kokanek/todo-list-with-signia) 存储库自行运行它。
测试 UI
让我们测试一下代码更改。当第一次运行应用程序时,可以看到待办事项列表中存在的 Milk
项,因为在以下状态下对其进行了硬编码:
可以通过添加更多项目来试用该应用程序:
可以通过单击复选框来检查任务:
UI
按预期工作,可以根据需要添加更多任务。
在 React 组件之间共享状态
需要探索的最后一件事是在不同的 React
组件之间共享状态。在本教程中构建的示例在同一文件中具有状态类以及该状态的使用者。
但是,在现实生活中的用例中,状态和消费的存储点相距甚远。在这些场景中,如何管理共享状态?
Signia
建议使用 React.context
。首先使用状态类创建一个上下文,然后将整个应用程序包装在该上下文提供程序中,将实例化的状态类作为值传递:
在示例中进行这些更改并进行测试。为此,在 App 组件中进行上述更改。然后,创建一个名为 TodoList.jsx
的新文件,并复制代码以呈现其中的列表项。还将代码用于从此文件的上下文中使用状态对象:
useTodoFromContext
帮助程序负责获取上下文并将状态的最新实例化返回给此组件。现在,将这个组件放在 App.tsx
文件中的蓝色 <div>
中。可以将其放置在 UX 中的任何位置,甚至可以在新路线上。
现在,当添加新的待办事项时,看到从上下文中读取此状态的 TodoList
组件也显示添加到列表中的最
新项:
在上面的演示中,正在读取 TodoList
组件中的列表项。因为可以从上下文访问 todo
对象,所以也可以调用 addItem
和 markItemAsDone
方法,它会反映在两个列表中。因此,我们有效地实现了来自中央来源的状态共享。
写在最后
在本文中,我们构建了一个使用 Signia
库及其 React
帮助程序来管理状态的应用程序。 useAtom Hook
提供了 useState
的替代方案,而以原子作为类属性的基于类的体系结构提供了一种构造更复杂的状态的方法。
还探索了一种在 React.createContext
和 useContext
的不同组件之间共享公共状态的方法,所有这些都没有反应性的初始设置和 Redux
等库所期望的样板。因此,Signia
可能是您下次构建 React
应用程序时用于状态管理的库。
版权声明: 本文为 InfoQ 作者【高端章鱼哥】的原创文章。
原文链接:【http://xie.infoq.cn/article/a1466dc6eeea3e3f5a849f37c】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论