jotai-expert
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseJotai Expert
Jotai 专业指导
Jotai is a primitive and flexible state management library for React using an atomic approach. Always reference https://jotai.org/ for the latest API details.
Jotai是一款采用原子化方案的轻量且灵活的React状态管理库。请始终参考https://jotai.org/获取最新的API细节。
Decision Tree
决策树
Need state management?
├── Simple local state → useState (no Jotai needed)
├── Shared state across components
│ ├── Few components, same tree → Context may suffice
│ └── Many components, complex → Use Jotai ✓
└── Global app state → Use Jotai ✓
Choosing atom type:
├── Static value → atom(initialValue)
├── Computed from other atoms → atom((get) => ...)
├── Need to modify other atoms → atom(null, (get, set) => ...)
├── Persist to storage → atomWithStorage()
├── List of items → splitAtom() or atoms-in-atom
└── Parameterized data → atomFamily()
Performance issue?
├── Component re-renders too often
│ ├── Only reading? → useAtomValue
│ ├── Only writing? → useSetAtom
│ ├── Large object? → selectAtom / focusAtom
│ └── List items? → splitAtom
└── Async loading states → loadable() or manual loading atoms是否需要状态管理?
├── 简单本地状态 → 使用useState(无需Jotai)
├── 组件间共享状态
│ ├── 少量组件、同属一个组件树 → Context可能足够
│ └── 大量组件、复杂场景 → 使用Jotai ✓
└── 全局应用状态 → 使用Jotai ✓
选择atom类型:
├── 静态值 → atom(initialValue)
├── 由其他atom计算得出 → atom((get) => ...)
├── 需要修改其他atom → atom(null, (get, set) => ...)
├── 需要持久化到存储 → atomWithStorage()
├── 列表项 → splitAtom() 或 atoms-in-atom
└── 参数化数据 → atomFamily()
存在性能问题?
├── 组件重渲染过于频繁
│ ├── 仅读取? → useAtomValue
│ ├── 仅写入? → useSetAtom
│ ├── 大型对象? → selectAtom / focusAtom
│ └── 列表项? → splitAtom
└── 异步加载状态 → loadable() 或手动实现加载atomCore Patterns
核心模式
Atom Types
Atom 类型
typescript
import { atom } from 'jotai'
// 1. Primitive: holds value
const countAtom = atom(0)
// 2. Derived read-only: computed from others
const doubleAtom = atom((get) => get(countAtom) * 2)
// 3. Derived read-write: custom setter
const celsiusAtom = atom(0)
const fahrenheitAtom = atom(
(get) => get(celsiusAtom) * 9/5 + 32,
(get, set, newF: number) => set(celsiusAtom, (newF - 32) * 5/9)
)
// 4. Write-only (action): no read value
const incrementAtom = atom(null, (get, set) => {
set(countAtom, get(countAtom) + 1)
})typescript
import { atom } from 'jotai'
// 1. 基础类型:存储值
const countAtom = atom(0)
// 2. 派生只读类型:由其他atom计算得出
const doubleAtom = atom((get) => get(countAtom) * 2)
// 3. 派生读写类型:自定义设置器
const celsiusAtom = atom(0)
const fahrenheitAtom = atom(
(get) => get(celsiusAtom) * 9/5 + 32,
(get, set, newF: number) => set(celsiusAtom, (newF - 32) * 5/9)
)
// 4. 仅写类型(动作):无读取值
const incrementAtom = atom(null, (get, set) => {
set(countAtom, get(countAtom) + 1)
})Hook Selection
Hook 选择
| Need | Hook | Re-renders on change |
|---|---|---|
| Read only | | Yes |
| Write only | | No |
| Both | | Yes |
| 需求 | Hook | 值变化时是否重渲染 |
|---|---|---|
| 仅读取 | | 是 |
| 仅写入 | | 否 |
| 读写都需要 | | 是 |
Reference Stability
引用稳定性
typescript
// ❌ WRONG: Creates new atom every render
function Component() {
const myAtom = atom(0) // Unstable reference
}
// ✅ CORRECT: Define outside component
const myAtom = atom(0)
function Component() {
const [value] = useAtom(myAtom)
}
// ✅ CORRECT: useMemo for dynamic atoms
function Component({ id }) {
const itemAtom = useMemo(() => atom(items[id]), [id])
}typescript
// ❌ 错误:每次渲染都会创建新的atom
function Component() {
const myAtom = atom(0) // 不稳定的引用
}
// ✅ 正确:在组件外部定义
const myAtom = atom(0)
function Component() {
const [value] = useAtom(myAtom)
}
// ✅ 正确:使用useMemo处理动态atom
function Component({ id }) {
const itemAtom = useMemo(() => atom(items[id]), [id])
}Performance Optimization
性能优化
1. Granular Subscriptions
1. 细粒度订阅
typescript
// ❌ BAD: Re-renders on any user field change
function UserProfile() {
const [user] = useAtom(userAtom)
return <h1>{user.name}</h1>
}
// ✅ GOOD: Only re-renders when name changes
import { selectAtom } from 'jotai/utils'
const nameAtom = selectAtom(userAtom, (u) => u.name)
function UserName() {
const name = useAtomValue(nameAtom)
return <h1>{name}</h1>
}typescript
// ❌ 不良实践:用户对象任意字段变化都会触发重渲染
function UserProfile() {
const [user] = useAtom(userAtom)
return <h1>{user.name}</h1>
}
// ✅ 良好实践:仅在name字段变化时重渲染
import { selectAtom } from 'jotai/utils'
const nameAtom = selectAtom(userAtom, (u) => u.name)
function UserName() {
const name = useAtomValue(nameAtom)
return <h1>{name}</h1>
}2. Efficient Lists with splitAtom
2. 使用splitAtom实现高效列表
typescript
import { splitAtom } from 'jotai/utils'
const todosAtom = atom<Todo[]>([])
const todoAtomsAtom = splitAtom(todosAtom, (t) => t.id)
function TodoList() {
const [todoAtoms] = useAtom(todoAtomsAtom)
return todoAtoms.map((todoAtom) => (
<TodoItem key={todoAtom.toString()} atom={todoAtom} />
))
}
// Each item updates independently
function TodoItem({ atom }) {
const [todo, setTodo] = useAtom(atom)
// Changes here don't re-render other items
}typescript
import { splitAtom } from 'jotai/utils'
const todosAtom = atom<Todo[]>([])
const todoAtomsAtom = splitAtom(todosAtom, (t) => t.id)
function TodoList() {
const [todoAtoms] = useAtom(todoAtomsAtom)
return todoAtoms.map((todoAtom) => (
<TodoItem key={todoAtom.toString()} atom={todoAtom} />
))
}
// 每个列表项独立更新
function TodoItem({ atom }) {
const [todo, setTodo] = useAtom(atom)
// 此处的修改不会触发其他列表项重渲染
}3. Large Objects with focusAtom
3. 使用focusAtom处理大型对象
typescript
import { focusAtom } from 'jotai-optics'
const formAtom = atom({ name: '', email: '', address: { city: '' } })
// Focused atoms for each field
const nameAtom = focusAtom(formAtom, (o) => o.prop('name'))
const cityAtom = focusAtom(formAtom, (o) => o.prop('address').prop('city'))
// Each field component only re-renders when its value changestypescript
import { focusAtom } from 'jotai-optics'
const formAtom = atom({ name: '', email: '', address: { city: '' } })
// 针对每个字段的聚焦atom
const nameAtom = focusAtom(formAtom, (o) => o.prop('name'))
const cityAtom = focusAtom(formAtom, (o) => o.prop('address').prop('city'))
// 每个字段组件仅在对应值变化时重渲染4. Async with loadable
4. 使用loadable处理异步
typescript
import { loadable } from 'jotai/utils'
const asyncDataAtom = atom(async () => fetch('/api').then(r => r.json()))
const loadableDataAtom = loadable(asyncDataAtom)
function Component() {
const data = useAtomValue(loadableDataAtom)
if (data.state === 'loading') return <Spinner />
if (data.state === 'hasError') return <Error />
return <Data value={data.data} />
}typescript
import { loadable } from 'jotai/utils'
const asyncDataAtom = atom(async () => fetch('/api').then(r => r.json()))
const loadableDataAtom = loadable(asyncDataAtom)
function Component() {
const data = useAtomValue(loadableDataAtom)
if (data.state === 'loading') return <Spinner />
if (data.state === 'hasError') return <Error />
return <Data value={data.data} />
}Anti-Patterns to Avoid
需避免的反模式
- Heavy computation in components: Move to atom read functions or actions
- Creating atoms in render: Define outside or use useMemo
- Using useAtom when only reading/writing: Use useAtomValue/useSetAtom
- Over-fragmenting frequently-updated data: Batch related updates
- Nested Suspense for every async atom: Use strategic boundaries or loadable
- 组件内执行大量计算:将计算逻辑移至atom的读取函数或action中
- 在渲染函数中创建atom:在组件外部定义,或使用useMemo
- 仅需读/写时使用useAtom:改用useAtomValue/useSetAtom
- 过度拆分频繁更新的数据:批量处理相关更新
- 为每个异步atom嵌套Suspense:使用合理的边界或loadable
References
参考资料
Detailed documentation organized by topic:
- references/core-api.md: atom, hooks, Store, Provider API details
- references/utilities.md: atomWithStorage, loadable, splitAtom, selectAtom, atomFamily
- references/performance.md: Optimization strategies, render control, large object handling
- references/patterns.md: Organization, async patterns, testing, TypeScript, SSR, debugging
When implementing or reviewing Jotai code, load the relevant reference file for detailed guidance.
按主题分类的详细文档:
- references/core-api.md:atom、hooks、Store、Provider的API细节
- references/utilities.md:atomWithStorage、loadable、splitAtom、selectAtom、atomFamily
- references/performance.md:优化策略、渲染控制、大型对象处理
- references/patterns.md:组织方式、异步模式、测试、TypeScript、SSR、调试
在实现或审查Jotai代码时,加载相关的参考文件以获取详细指导。