mcpField
J
jotai-expert

jotai-expert

github.com/s-hiraoku开发0204 次安装37 周前更新
代码仓库

关于 jotai-expert

jotai-expert 是用于使用 Jotai 实现和审查 React 状态管理的 Skill,涵盖 Atom 与 Hook 的选择、性能模式、异步状态及常见实现误区。

主要功能

  1. 01Atom 类型与 Hook 选择
  2. 02细粒度订阅
  3. 03高效管理列表和对象状态
  4. 04使用 loadable 处理异步状态
  5. 05常见反模式指引

适用场景

  1. 01为共享 React 状态选择管理方式
  2. 02减少组件重新渲染
  3. 03管理大型表单和列表
  4. 04处理异步 Atom 数据

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/s-hiraoku/skills-factory --skill jotai-expert

安装后在 Claude Code 中输入/jotai-expert确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
core-api.md
patterns.md
performance.md
utilities.md

详细介绍

Jotai 专家

Jotai 是一个采用原子化方法的 React 状态管理库,原始且灵活。请始终参考 https://jotai.org/ 获取最新的 API 详情。

决策树

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

核心模式

Atom 类型
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)
})
Hook 选择
需求Hook更改时是否重新渲染
只读useAtomValue(atom)是
只写useSetAtom(atom)否
读写useAtom(atom)是
引用稳定性
// ❌ 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])
}

性能优化

1. 细粒度订阅
// ❌ 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>
}
2. 使用 splitAtom 处理高效列表
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
}
3. 使用 focusAtom 处理大型对象
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
4. 使用 loadable 处理异步操作
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} />
}

应避免的反模式

  1. 在组件中执行繁重计算:将计算移至 atom 读取函数或操作中
  2. 在渲染过程中创建 atom:在组件外定义,或使用 useMemo
  3. 只读或只写时仍使用 useAtom:改用 useAtomValue/useSetAtom
  4. 过度拆分频繁更新的数据:批量处理相关更新
  5. 为每个异步 atom 嵌套 Suspense:采用有针对性的边界或使用 loadable

参考资料

按主题整理的详细文档:

实现或审查 Jotai 代码时,请加载相关参考文件以获取详细指导。

评论

支持 Markdown