Jotai学习心得
Jotai学习心得
了解和使用Jotai之前,我对跨组件状态管理的主要手段是useState + props、Context,以及在部分场景下用EventBus做组件间通信。但在项目复杂度逐渐上升后,这些方案要么带来明显的prop drilling,要么导致组件订阅范围过大、渲染不可控。
Jotai 采用原子方法进行全局 React 状态管理。
Jotai 给我的一个最直观感受是:它不是在引入一个“更大的状态中心”,而是在把状态拆得足够小。
Atoms are the building blocks of universe and clump together into molecules–
原子是宇宙的构建模块,聚集成分子——
Jotai 原子是小而孤立的状态片段。理想情况下,一个原子包含非常小的数据(虽然这只是个惯例。仍然可以把所有状态放到一个原子里,虽然那样性能会非常慢)
import { atom } from 'jotai'; |
atom 是“最小可共享状态单元”,而不是全局变量
一开始我担心atom会不会变成新的“全局变量污染”,但实际使用下来发现并不是这样。
atom本身并不会主动影响任何组件,只有显式使用 useAtom / useAtomValue / useSetAtom 的组件才会订阅它。
这和传统EventBus或Context的“广播式更新”有本质区别——状态变化只会影响真正关心它的组件,渲染范围更可控。
很多 EventBus 场景,本质上是“状态同步”而不是“事件”
在回顾之前使用EventBus的场景时,我发现大量所谓的“跨组件通信”,其实是在同步一些可持续存在的状态,比如:
- 是否打开某个弹窗
- 当前选中的对象
- 列表是否需要刷新
这些场景用EventBus往往会引入额外的时序问题,而用Jotai表达为atom后,状态来源和消费关系变得更清楚,也更容易调试。
- 状态型通信 → Jotai
- 一次性行为 / 命令式操作 → 普通事件或函数调用
什么时候用Jotai
Jotai 不是“我要一个全局变量”,而是“我要一个可以被多个组件共享的最小状态单元”
面对以下场景:
“这个状态是不是应该被多个地方同时读 / 改?”
- 是 → Jotai
- 否 → useState
Jotai 更适合“局部全局”,而不是无脑全局
通过学习和了解,总结了一个结论是:不是所有state都值得atom化。
组件内部的临时状态(如hover、输入框值)继续使用useState反而更简单。
Jotai 真正的优势在于:
- 多个组件确实需要共享
- 或者希望避免状态层层传递
- 或者需要精确控制谁会因状态变化而更新
在这个前提下,atom更像是“业务域内的共享状态”,而不是应用级的万能全局状态。
怎么用Jotai
当原子在同一组件内同时读写时,为了简化使用结合的useAtom 钩子。
import { atom, useAtom } from 'jotai'; |
看起来很像useState,但唯一的区别是我们创建的原子要传递给 useState。useAtom返回一个大小为 2 的数组,其中第一个元素是一个值,第二个元素是一个函数,用于设置原子的值。这使得所有依赖该原子的组件都会更新并重新渲染。
当从不同的组件读取和写入时,使用分别的useAtomValue 和useSetAtom 钩子来优化重新渲染。
import { useAtomValue, useSetAtom } from 'jotai' |
好用的atomWithImmer
atomWithImmer 不是让状态“更全局”,而是让“共享的复杂状态”写起来更安全、更清晰。
React状态最大的痛点:不可变性。
不可变性很棒,也让人很容易理解React状态,但当状态是对象时,事情会变得非常困难。然后就得做一整套流程,展开对象并与想更新的属性合并。
而使用Immer允许你直接变异状态
import { atomWithImmer } from 'jotai/immer'; |
✅ 适合场景
- 深层嵌套对象
- 列表、树结构
- 表单状态
- 需要频繁局部修改的共享状态
❌ 不太适合
- 简单标量值(number / boolean)
- 非共享的局部状态(用useImmer或useState更简单)
- 极度追求极致性能的热点路径(Immer有微小开销)
