React 原子化状态管理:用 Jotai 把全局状态拆成可组合的原子
test12026-10-143 次阅读
为什么不直接用单一 store
Redux 或 Zustand 这类方案通常是一个集中的 store,组件通过 selector 去取自己关心的片段。项目变大以后,store 里塞满了互不相干的业务状态,某个字段一变,所有订阅它的组件都要重新评估。Jotai 的思路相反:状态不是一个大对象,而是一堆最小的「原子」(atom),谁用谁取,谁变谁重渲染,天然就避开了大 store 的连带更新问题。
安装与最小示例
先装包,然后在外层挂一个 Provider。不挂也能用,但用 Provider 才能在多个地方共享同一份原子实例。
npm install jotai
// main.tsx
import { Provider } from 'jotai'
import { createRoot } from 'react-dom/client'
createRoot(document.getElementById('root')!).render(
<Provider>
<App />
</Provider>
)
定义一个原子就像定义一个 useState,但它是模块级的、可被任意组件引用:
// store/counter.ts
import { atom } from 'jotai'
export const countAtom = atom(0)
export const nameAtom = atom('guest')
// Counter.tsx
import { useAtom } from 'jotai'
import { countAtom } from './store/counter'
export function Counter() {
const [count, setCount] = useAtom(countAtom)
return <button onClick={() => setCount(c => c + 1)}>点击了 {count} 次</button>
}
原子组合:派生状态用派生原子
一个原子可以依赖另一个原子,写法就是一个读函数。它不会单独存值,而是实时计算,上游原子变了它自动跟着变。
import { atom } from 'jotai'
export const countAtom = atom(0)
// 派生原子:双倍计数
export const doubleAtom = atom((get) => get(countAtom) * 2)
// 可写派生:设置时反向除以 2
export const writableDouble = atom(
(get) => get(countAtom) * 2,
(get, set, newVal: number) => set(countAtom, newVal / 2)
)
按需订阅,避免无谓重渲染
只有真正调用 useAtom 读某个原子的组件才会订阅它。下面两个组件读不同的原子,改 count 不会让 NameBox 重新渲染,这是拆分原子带来的直接收益。
import { useAtom } from 'jotai'
import { countAtom, nameAtom } from './store/counter'
function CountBox() {
const [count] = useAtom(countAtom)
return <span>count={count}</span>
}
function NameBox() {
const [name] = useAtom(nameAtom)
return <span>name={name}</span>
}
异步原子与请求缓存
atom 的初值可以是一个返回 Promise 的函数,Jotai 会处理 pending/resolved,配合 Suspense 就能做到请求与组件渲染自然衔接。
import { atom } from 'jotai'
export const userIdAtom = atom(1)
export const userAtom = atom(async (get) => {
const id = get(userIdAtom)
const res = await fetch(`/api/users/${id}`)
return res.json()
})
在组件里直接读,外层包一个 Suspense 兜底加载态即可:
import { Suspense } from 'react'
import { useAtom } from 'jotai'
import { userAtom } from './store/user'
function Profile() {
const [user] = useAtom(userAtom)
return <div>{user.name}</div>
}
export default () => (
<Suspense fallback={<div>加载中</div>}>
<Profile />
</Suspense>
)
什么时候选 Jotai
当业务状态彼此独立、分散在多个页面,且你不想为了改一个字段去设计一大张 selector 图时,Jotai 的原子模型能让状态就近定义、就近消费。若状态高度集中、需要时间旅行调试或中间件,那 Redux 仍更合适。两者并不冲突,同一个项目里也可以按模块选择。
T
test1
文章作者
为什么不直接用单一 store Redux 或 Zustand 这类方案通常是一个集中的 store,组件通过 sel...
- 分类
- 技术
- 发布时间
- 2026-10-14
- 字数
- 约 2490 字
- 阅读
- 3 次
