Summary
- Jotai状態管理ライブラリのエキスパートスキル。Reactアプリケーションでのatomベースの状態管理を実装する際に使用。以下の場合にこのスキルを使用:
- (1) Jotaiのatom設計・実装
- (2) 派生atom、非同期atom、atomFamilyの実装
- (3)…
s-hiraoku/claude-code-harnesses-factory · Archived
Jotai状? (1) Jotaiのatom設計・実? (2) 派生atom、非同期atom、atomFamilyの実? (3) Jotaiのベストプラクティスに基づくリファクタリング (4) パフォーマンス最適化(selectAtom、splitAtom等) (5) 永続化(localStorage/sessionStorage連携) (6) TypeScript型定義 (7) テスト実? ユーザーが「Jotai」「atom」「状?
npx skills add s-hiraoku/claude-code-harnesses-factory --skill jotai-expert
This repository is archived — consider an actively maintained alternative.
This skill provides structured question-asking capabilities for gathering user input, clarifyin…
4 installsGenerate beautiful infographic PNG images from Claude Code changelog summaries. Use this skill …
3 installsInterpret Claude Code changelogs and generate user-friendly usage guides
2 installsThis skill guides creating Claude skills—modular packages that extend capabilities with special…
2 installsRelated neighbors and high-traction skills in the same topics — useful to compare before installing.
Expert guidance for working with Dagster and the dg CLI. ALWAYS use before doing any task that …
5.1K installsAdd a new expert to the Remotion experts page
2.1K installsValidate and use MoE expert-parallel communication overlap in Megatron-Bridge, including overla…
1.8K installsUse when writing, reviewing, or refactoring SwiftUI code for iOS or macOS, including state mana…
30.9K installsYou are an advanced Docker containerization expert with comprehensive, practical knowledge of c…
26.8K installsConvex backend specialist. Use this agent for any code inside a `convex/` directory — function …
18.5K installsOther skills from s-hiraoku/claude-code-harnesses-factory.
npx skills add s-hiraoku/claude-code-harnesses-factory
Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.
main
Parsed from SKILL.md frontmatter.
Files included with this skill beyond the listing page.
SKILL.md
7,189 B
SUMMARY.md
660 B
Jotaiを使用したReact状態管理の実装ガイド。
状態の最小単位。値を持たず、Storeに保存される。
// 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)
})
// Read and write
const [value, setValue] = useAtom(countAtom)
// Read only
const value = useAtomValue(countAtom)
// Write only
const setValue = useSetAtom(countAtom)
// atoms/user.ts
const baseUserAtom = atom<User | null>(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)
})
const userIdAtom = atom<number | null>(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 <div>{userData?.name}</div>
}
// Suspenseでラップ
<Suspense fallback={<Loading />}>
<UserProfile />
</Suspense>
動的にatomを生成・キャッシュ。メモリリーク対策必須。
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時間後削除
})
import { atomWithStorage } from 'jotai/utils'
// localStorage永続化
const themeAtom = atomWithStorage('theme', 'light')
// sessionStorage永続化
import { createJSONStorage } from 'jotai/utils'
const sessionAtom = atomWithStorage(
'session',
null,
createJSONStorage(() => sessionStorage)
)
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)
}
)
大きなオブジェクトから一部のみ取得。派生atomを優先し、必要な場合のみ使用。
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),
[]
)
配列の各要素を独立したatomとして管理。
import { splitAtom } from 'jotai/utils'
const todosAtom = atom<Todo[]>([])
const todoAtomsAtom = splitAtom(todosAtom)
function TodoList() {
const [todoAtoms, dispatch] = useAtom(todoAtomsAtom)
return (
<>
{todoAtoms.map((todoAtom) => (
<TodoItem
key={`${todoAtom}`}
todoAtom={todoAtom}
onRemove={() => dispatch({ type: 'remove', atom: todoAtom })}
/>
))}
</>
)
}
// 型推論を活用(明示的型定義は不要な場合が多い)
const countAtom = atom(0) // PrimitiveAtom<number>
// 明示的型定義が必要な場合
const userAtom = atom<User | null>(null)
// Write-only atomの型
const actionAtom = atom<null, [string, number], void>(
null,
(get, set, str, num) => { ... }
)
// 型抽出
type CountValue = ExtractAtomValue<typeof countAtom> // number
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 (
<Provider>
<HydrateAtoms initialValues={initialValues}>
{children}
</HydrateAtoms>
</Provider>
)
}
// テスト
test('increments counter', async () => {
render(
<TestProvider initialValues={[[countAtom, 5]]}>
<Counter />
</TestProvider>
)
await userEvent.click(screen.getByRole('button'))
expect(screen.getByText('6')).toBeInTheDocument()
})
// デバッグラベル追加
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' })
remove()またはsetShouldRemove()を使用詳細は以下を参照: