jotai-expert

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Jotai 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() 或手动实现加载atom

Core 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 选择

NeedHookRe-renders on change
Read only
useAtomValue(atom)
Yes
Write only
useSetAtom(atom)
No
Both
useAtom(atom)
Yes
需求Hook值变化时是否重渲染
仅读取
useAtomValue(atom)
仅写入
useSetAtom(atom)
读写都需要
useAtom(atom)

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 changes
typescript
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

需避免的反模式

  1. Heavy computation in components: Move to atom read functions or actions
  2. Creating atoms in render: Define outside or use useMemo
  3. Using useAtom when only reading/writing: Use useAtomValue/useSetAtom
  4. Over-fragmenting frequently-updated data: Batch related updates
  5. Nested Suspense for every async atom: Use strategic boundaries or loadable
  1. 组件内执行大量计算:将计算逻辑移至atom的读取函数或action中
  2. 在渲染函数中创建atom:在组件外部定义,或使用useMemo
  3. 仅需读/写时使用useAtom:改用useAtomValue/useSetAtom
  4. 过度拆分频繁更新的数据:批量处理相关更新
  5. 为每个异步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代码时,加载相关的参考文件以获取详细指导。