- Published on
原子化状态管理方案-jotai
- Authors
- Name
- noodles
- 每个人的花期不同,不必在乎别人比你提前拥有
在之前的Form表单方案思考中,在实现表单方案上有一些思考:
- 功能字段原子化
- 设计生命周期能力
- 结合当前业务情况去选型
在原子能力的理解上,原子是最小化的单元模块.它包含极简的功能,通过原子的组合(派生)来组合出更加复杂的功能模版.原子模块在复用性和功能扩展性上有一定的优势. jotai就是一种基于原子能力设计的状态管理方案.下面从jotai的简单使用入手进而梳理jotai的实现原理.在文末会与其他状态管理方案对比总结.
前置知识
WeakMap
在jotai中,使用WeakMap来做store数据 的存储.WeakMap的键值只能是对象或者非全局的Symbol对象,并且对对象的引用是弱引用,不能阻止垃圾回收机制.
jotai的简单使用
// 定义一个atom值
const countAtom = atom(0)
export default function Home() {
// 使用useAtom来获取atom值 相当于useState 在值变化的时候会自动触发组件渲染
const [count, setCount] = useAtom(countAtom)
return (
<div
onClick={() => setCount(count + 1)}
>
{count}
</div>
)
}
jotai的实现原理
const countAtom = atom(0)
const doubledCountAtom = atom((get) => get(countAtom) * 2)
const store = getDefaultStore()
store.set(countAtom, 10)
const doubledCountAtomValue = store.get(doubledCountAtom)
// value = 20
const countAtomValue = store.get(countAtom)
// value = 10
上面的例子中基础的atom值countAtom、派生atom值doubledCountAtom,通过store的方式设置countAtom的值,最后发现派生的doubledCountAtom值也发生了 变化.下面就从这个例子出发了解jotai的值创建逻辑、更新逻辑的实现,最后看下jotai是如何与react结合实现组件的更新的.
值的创建
// 创建atom值的入口函数
// 通过函数重载, atom入口函数支持多种参数类型
export function atom<Value, Args extends unknown[], Result>(
read?: Value | Read<Value, SetAtom<Args, Result>>,
write?: Write<Args, Result>,
) {
// atom的唯一标识,在值更新依赖追踪都有用
const key = `atom${++keyCount}`
const config = {
toString() {
return import.meta.env?.MODE !== 'production' && this.debugLabel
? key + ':' + this.debugLabel
: key
},
} as WritableAtom<Value, Args, Result> & { init?: Value | undefined }
// read是函数时候处理逻辑 派生处理
if (typeof read === 'function') {
config.read = read as Read<Value, SetAtom<Args, Result>>
} else {
// 普通方式 处理逻辑
config.init = read
config.read = defaultRead
config.write = defaultWrite as unknown as Write<Args, Result>
}
// 写派生处理逻辑
if (write) {
config.write = write
}
return config
}
// 省略若干代码
// 创建store的函数功能
const store: Store = {
// 获取atom值
get: (atom) => returnAtomValue(readAtomState(atom)),
// 设置atom值
set: (atom, ...args) => {
try {
return writeAtomState(atom, ...args)
} finally {
// 依赖更新处理逻辑
recomputeInvalidatedAtoms()
flushCallbacks()
}
},
// 订阅atom值
sub: (atom, listener) => {
const mounted = mountAtom(atom)
const listeners = mounted.l
listeners.add(listener)
flushCallbacks()
return () => {
listeners.delete(listener)
unmountAtom(atom)
flushCallbacks()
}
},
}
Object.defineProperty(store, BUILDING_BLOCKS, { value: buildingBlocks })
return store
值的获取
在获取atom值的时候,执行store的get方法.


值的设置
在设置atom值的时候,执行store的set方法.


跟react的结合实现实现组件更新
在组件中,会使用useAtom来获取atom值,在atom值变化的时候会自动触发组件的更新. 

jotai实现上的一些思考
异步数据更新实现
jotai可以实现异步数据的使用,在实现上通过标志位的方式,非常巧妙.在工具包中也融合了一些常用的store使用方式.
增加测试调试能力入口
在调试包的时候,有很多内部能力暴露给外部,在设计功能包的时候可以考虑做类似功能的设计,增加测试、调试能力.
懒计算
当前atom的依赖发生更新的时候,只会更新依赖atom的版本.只有在重新获取当前atom的时候,才会重新计算当前atom的值
状态管理方案的一点思考
当我们聊状态管理的时候我们在聊什么中阅读了redux/mobx/zustand的源码实现,jotia似乎是比较 '另类'的状态管理方案.也许到了那个老生常谈的话题,如果真的要做状态管理方案的选型应该怎么选呢?!!! 也许应该多问自己几个问题:
- 当前的业务是什么样的/未来的业务数据是怎么样的 比如数据轻量还是复杂度较高
- 团队是否有学习成本 好的库会'教育'人,这种技术选型是否会在团队比较容易的形成最佳实践-团队的方言