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 次