使用 Overmind.js 进行现代状态管理
在应用程序中管理状态就像操控数据一样。本文将介绍一种使用Overmind.js在 React 应用中管理状态的非常有趣的方法。
为什么选择 Overmind?
市面上有很多状态管理工具,例如Context-API、Redux、MobX和MST。那么,我们为什么还需要另一个呢?
在我最近的客户项目中,我使用了基于上下文的状态管理,并结合了Apollo Client。我非常喜欢 React Hooks,它与 Context-API 结合使用,是管理应用程序状态的绝佳方式。我当时觉得不再需要像Redux这样更复杂的状态管理方案了。起初,这种方式完全没问题,我很满意。但两个月后,应用程序规模扩大,复杂性也随之增加。我不再满意,于是决定切换到其他解决方案。
以前我经常使用Redux ,也很喜欢它,用起来很顺手,但它总给人一种“代码量过大,即使是小事也难以处理”的感觉。另一方面,我虽然没怎么用过MobX,但听说它评价很好。经过一番研究,我发现了一个叫Overmind 的新库,感觉很有意思。
集各方面优势于一身
Overmind 由Christian Alfoni创建,旨在提供最佳的开发者体验,并拥有强大的 TypeScript 支持。该框架的内部机制对开发者是隐藏的,API 也非常简洁明了。
- 强大的 TypeScript 支持
- 非常简单的 API
- 文档齐全,易于学习
- 优秀的开发者工具
我认为 Overmind 最棒的功能之一就是几乎免费提供完整的预编译代码。
定义状态
将你的状态定义为一个简单的对象。即使是 TypeScript,你也可以像这样简单地定义你的状态:
const state = {
// title: string
title: "",
// count: number
count: 0,
// foo: Foo | null
foo: null as Foo | null,
}
提示:不要在状态中使用 undefined 或 ?,因为这被认为是一种反模式。状态中的任何 undefined 值都不会显示在开发者工具中,也不会传递给服务器或保存到本地存储中。
无论在何处使用该状态,您都将获得完整的 TypeScript 支持和代码补全功能。很简单,对吧?
派生状态
派生状态是基于其他状态计算出的值。在 Overmind 中,您可以直接在状态旁边定义派生状态。
这里我们定义了一个计算状态变量doubleCount。需要注意的是,该函数默认是记忆化的,并且仅在count发生变化时运行。
const state = {
count: 0,
// memoized function that only executed when count change
doubleCount: (state) => state.count * 2
}
在Redux中,你需要编写选择器并使用像Reselect这样的库来缓存计算结果。但在Overmind中则无需如此,它已经内置了这些功能。很简单,对吧?
状态突变
所有状态变更都通过action完成。Action 可以访问你的状态并直接更改属性。
function incCount(state) {
state.count = state.count + 1
// hint: you get fully typed state and actions here
state.count = "three" // TS Error: Should be number
}
function resetCount(state) {
state.count = 0
}
function setCount(state, value) {
state.count = value
}
提示:你不能在操作之外改变状态,这意味着你不能在视图中意外地尝试更改它们。
这里没有像Redux那样因不可变性而导致的混乱。你想改什么就改什么。很简单,对吧?
副作用
Effects 可以让您的应用与第三方 API 完全解耦。您可以在这里阅读更多相关信息:overmind effects。
文档中提到:应用程序开发不仅涉及状态管理,还涉及副作用管理。典型的副作用包括 HTTP 请求或对 localStorage 的操作。在 Overmind 中,我们把这些统称为副作用(effects)。
效果器应该在 Overmind 的 onInitialize 函数中进行“初始化”。你可以在那里为它们提供所有需要的信息,例如获取当前状态的 getter 方法或要执行的操作。
export const onInitialize = ({ state, effects, actions }) => {
effects.myCoolEffect.initialize({
getSomeState: state.partOfState,
onMoviesLoadSuccess: actions.setMovies
})
}
组件中的访问状态和操作
要在组件中获取状态,需要将其连接到 Overmind。这可以通过useOvermind hook 来实现,该 hook 提供状态和操作。您只需解构 hook 的结果,即可获得所需的一切。
function Counter() => {
// hint: you get fully typed state and actions here
const {
state: { count },
actions: { incCount }
} = useOvermind()
return (
<div>
Count: {count}
<button onClick={incCount}>INC</button>
</div>
)
}
就这些?没错,将状态和操作添加到组件中竟然如此简单,简直令人难以置信。但是等等:当全局状态的其他部分(例如标题)发生变化时,我们如何阻止组件重新渲染?我们的组件只关注计数属性,并且只希望在该值发生变化时重新渲染。
你猜怎么着?Overmind 精确地知道组件正在使用状态的哪些部分,并且仅当该部分状态发生变化时才更新组件。很简单,对吧?
突变追踪
Overmind 使用的是突变跟踪而不是不可变性,您可以在这里阅读更多关于此概念的信息:不可变性与突变跟踪。
Christian Alfoni:检测变更并更新用户界面的方式应该简单高效!有了变更跟踪,你就可以完全信任系统。它让你无需费心去研究组件的更新方式,一切都运行正常,而且性能最优。
强大的开发者工具
Overmind 内置了非常强大的开发者工具。您可以使用VSCode 扩展,也可以使用独立版本。
npx overmind-devtools
您可以查看所有状态和派生状态,甚至可以在工具内直接修改它们。您还可以查看所有已执行的操作及其有效负载,以及它们更改了状态的哪些部分。想发送操作?当然可以,您也可以做到。
应用程序的视图只是一个实现细节。你完全可以不用视图,仅借助开发者工具就能编写和执行整个应用程序逻辑。这真是既神奇又简单,对吧?
函数式编程风格
我非常喜欢函数式编程范式,这种范式随着 React 的引入而出现,并在 Hooks 发布后成为 React 世界的默认范式。Overmind 完美契合了这种理念。你只需要编写函数,无需使用类。
当我查看 MobX 时,我发现这对我来说是一个很大的缺点,因为它的所有示例都使用了类,而如果可以的话,我不想再使用类了。
纯函数呢?嗯,当然,Overmind 的 action 不如普通的 reducer 那么纯粹。不过对我来说,这在实践中不算什么缺点,因为无论如何你都可以很轻松地测试你的 action。
文档和学习曲线
Overmind 的文档非常完善。我阅读了文档,开始在我的应用程序中尝试使用,3 个小时后,我就重构了整个应用程序。
框架内部机制对开发者来说是隐藏的,API 也非常简洁明了。无需学习像reselect、redux-sagas、redux-thunk等其他隐藏技术。
值得一提的是:这真的非常简单,因为我使用了react-testing-library,所以修改起来很有信心。这些测试只测试组件的功能,不涉及实现细节。我只需要在测试初始化部分做一些非常小的调整。
几个小时后你应该就能感觉很得心应手了。想当年,我可是花了几个星期才搞懂 Redux。我还试过 MobX(当时正在学习 Overmind),如果你完全不了解这些,阅读文档并理解其中的观察者和可观察者机制就困难得多。
运行示例
您可以在这里看到一个在 Codesandbox 上运行的示例:
概括
我很高兴发现了Overmind,它用起来真的很有趣,而且彻底简化了我的应用程序。希望这篇文章能让你也想尝试一下。
实用链接
- Codesandbox TypeScript 示例
- Overmind 文档,介绍得很好
- 创作者克里斯蒂安·阿尔弗尼本人的视频
- 不变性与突变追踪
- 封面图片来自Pixabay的Theodor Moise。