🎯

jotai-expert

🎯Skill

from s-hiraoku/claude-code-harnesses-factory

VibeIndex|
What it does

Expertly implements Jotai atom-based state management in React, covering advanced patterns like async atoms, atomFamily, and persistence strategies.

📦

Part of

s-hiraoku/claude-code-harnesses-factory(9 items)

jotai-expert

Installation

Add MarketplaceAdd marketplace to Claude Code
/plugin marketplace add s-hiraoku/harnesses-factory
Claude CodeAdd plugin in Claude Code
/plugin search
Install PluginInstall plugin from marketplace
/plugin install cc-version-updater
Install PluginInstall plugin from marketplace
/plugin install cc-version-updater@s-hiraoku/harnesses-factory
📖 Extracted from docs: s-hiraoku/claude-code-harnesses-factory
1Installs
-
AddedFeb 4, 2026

Skill Details

SKILL.md

|

Overview

# Jotai Expert

Jotaiを使用したReact状態管理の実装ガイド。

Core Concepts

Atom

状態の最小単位。値を持たず、Storeに保存される。

```typescript

// Primitive atom

const countAtom = atom(0)

const nameAtom = atom('')

// Derived read-only atom

const doubleAtom = atom((get) => get(countAtom) * 2)

// Derived read-write atom

const countWithLabelAtom = atom(

(get) => Count: ${get(countAtom)},

(get, set, newValue: number) => set(countAtom, newValue)

)

// Write-only atom (action atom)

const incrementAtom = atom(null, (get, set) => {

set(countAtom, get(countAtom) + 1)

})

```

Hooks

```typescript

// Read and write

const [value, setValue] = useAtom(countAtom)

// Read only

const value = useAtomValue(countAtom)

// Write only

const setValue = useSetAtom(countAtom)

```

Implementation Patterns

Pattern 1: Feature Module

```typescript

// atoms/user.ts

const baseUserAtom = atom(null)

// Public read-only atom

export const userAtom = atom((get) => get(baseUserAtom))

// Actions

export const setUserAtom = atom(null, (get, set, user: User) => {

set(baseUserAtom, user)

})

export const clearUserAtom = atom(null, (get, set) => {

set(baseUserAtom, null)

})

```

Pattern 2: Async Data Fetching

```typescript

const userIdAtom = atom(null)

// Suspenseと連携する非同期atom

const userDataAtom = atom(async (get) => {

const userId = get(userIdAtom)

if (!userId) return null

const response = await fetch(/api/users/${userId})

return response.json()

})

// Component

function UserProfile() {

const userData = useAtomValue(userDataAtom)

return

{userData?.name}

}

// Suspenseでラップ

}>

```

Pattern 3: atomFamily

動的にatomを生成・キャッシュ。メモリリーク対策必須。

```typescript

const todoFamily = atomFamily((id: string) =>

atom({ id, text: '', completed: false })

)

// 使用

const todoAtom = todoFamily('todo-1')

// クリーンアップ

todoFamily.remove('todo-1')

// 自動削除ルール設定

todoFamily.setShouldRemove((createdAt, param) => {

return Date.now() - createdAt > 60 60 1000 // 1時間後削除

})

```

Pattern 4: Persistence

```typescript

import { atomWithStorage } from 'jotai/utils'

// localStorage永続化

const themeAtom = atomWithStorage('theme', 'light')

// sessionStorage永続化

import { createJSONStorage } from 'jotai/utils'

const sessionAtom = atomWithStorage(

'session',

null,

createJSONStorage(() => sessionStorage)

)

```

Pattern 5: Reset

```typescript

import { atomWithReset, useResetAtom, RESET } from 'jotai/utils'

const formAtom = atomWithReset({ name: '', email: '' })

// コンポーネント内

const resetForm = useResetAtom(formAtom)

resetForm() // 初期値に戻る

// 派生atomでRESETシンボル使用

const derivedAtom = atom(

(get) => get(formAtom),

(get, set, newValue) => {

set(formAtom, newValue === RESET ? RESET : newValue)

}

)

```

Performance Optimization

selectAtom

大きなオブジェクトから一部のみ取得。派生atomを優先し、必要な場合のみ使用。

```typescript

import { selectAtom } from 'jotai/utils'

const personAtom = atom({ name: 'John', age: 30, address: {...} })

// nameのみを購読

const nameAtom = selectAtom(personAtom, (person) => person.name)

// 安定した参照が必要(useMemoまたは外部定義)

const stableNameAtom = useMemo(

() => selectAtom(personAtom, (p) => p.name),

[]

)

```

splitAtom

配列の各要素を独立したatomとして管理。

```typescript

import { splitAtom } from 'jotai/utils'

const todosAtom = atom([])

const todoAtomsAtom = splitAtom(todosAtom)

function TodoList() {

const [todoAtoms, dispatch] = useAtom(todoAtomsAtom)

return (

<>

{todoAtoms.map((todoAtom) => (

key={${todoAtom}}

todoAtom={todoAtom}

onRemove={() => dispatch({ type: 'remove', atom: todoAtom })}

/>

))}

)

}

```

TypeScript

```typescript

// 型推論を活用(明示的型定義は不要な場合が多い)

const countAtom = atom(0) // PrimitiveAtom

// 明示的型定義が必要な場合

const userAtom = atom(null)

// Write-only atomの型

const actionAtom = atom(

null,

(get, set, str, num) => { ... }

)

// 型抽出

type CountValue = ExtractAtomValue // number

```

Testing

```typescript

import { render, screen } from '@testing-library/react'

import userEvent from '@testing-library/user-event'

import { Provider } from 'jotai'

import { useHydrateAtoms } from 'jotai/utils'

// 初期値を注入するヘルパー

function HydrateAtoms({ initialValues, children }) {

useHydrateAtoms(initialValues)

return children

}

function TestProvider({ initialValues, children }) {

return (

{children}

)

}

// テスト

test('increments counter', async () => {

render(

)

await userEvent.click(screen.getByRole('button'))

expect(screen.getByText('6')).toBeInTheDocument()

})

```

Debugging

```typescript

// デバッグラベル追加

countAtom.debugLabel = 'count'

// useAtomsDebugValueでProvider内の全atom確認

import { useAtomsDebugValue } from 'jotai-devtools'

function DebugObserver() {

useAtomsDebugValue()

return null

}

// Redux DevTools連携

import { useAtomDevtools } from 'jotai-devtools'

useAtomDevtools(countAtom, { name: 'count' })

```

Best Practices

  1. Atom粒度: 小さく再利用可能な単位に分割
  2. カプセル化: base atomを隠蔽し、派生atomのみをexport
  3. Action atom: 複雑な更新ロジックはwrite-only atomに分離
  4. 非同期処理: SuspenseとError Boundaryを適切に配置
  5. atomFamily: メモリリーク対策としてremove()またはsetShouldRemove()を使用
  6. TypeScript: 型推論を活用し、必要な場合のみ明示的に型定義
  7. テスト: ユーザー操作に近い形でテストを記述

References

詳細は以下を参照:

  • [patterns.md](references/patterns.md): 高度な実装パターン
  • [api.md](references/api.md): API詳細リファレンス