JotaiとReact Suspense/Transitionを組み合わせたモダンな状態管理・非同期処理のベストプラクティス集。uhyo氏の「jotaiによるReact再入門」に基づく。
View on GitHubFebruary 1, 2026
Select agents to install to:
npx add-skill https://github.com/kazuph/dotfiles/blob/main/plugins/kazuph-dotfiles/skills/jotai-react-mastery/SKILL.md -a claude-code --skill jotai-react-masteryInstallation paths:
.claude/skills/jotai-react-mastery/# Jotai React Mastery Skill
## 概要
Jotaiを用いたReactアプリケーション設計、特にSuspense、Concurrent features(Transition)、非同期処理、エラーハンドリングに関するベストプラクティスを提供するスキルです。
「jotaiによるReact再入門」の内容をベースに、宣言的UIの原則に従った実装パターンを提示します。
## 利用シーン
- Jotaiを用いた状態管理の設計時
- React Suspenseを利用した非同期データ取得の実装時
- `useTransition` を用いたUX改善(ちらつき防止、ペンディング表示)
- 非同期処理のエラーハンドリングとリトライ機構の実装
- `jotai-eager` を用いたパフォーマンス最適化
## ベストプラクティス & パターン
### 1. Jotaiの基本原則
- **定義と利用の分離**: `atom`でステートを定義し、`useAtom`で利用する。`useState`の役割を分割・拡張する。
- **派生atomによるカプセル化**: 生のatom(Primitive Atom)を隠蔽し、読み取り専用またはアクション用(書き込み専用)の派生atom(Derived Atom)のみを公開することで、意図しないステート変更を防ぐ。
- **最小限のAPI**: 複雑な操作はカスタムフックではなく、書き込み可能な派生atom(Action Atom)として実装する。引数を取る書き込み関数を活用する。
### 2. Suspenseと非同期処理 (Render-as-You-Fetch)
Suspenseを正しく動作させるため、Promiseはコンポーネント内(useEffectやuseMemo内)ではなく、**コンポーネントの外(Atom)**で管理する。
#### 基本パターン
```ts
// コンポーネント外でPromiseを保持する
const userAtom = atom(async () => {
const user = await fetchUser();
return user;
});
// コンポーネント内
const UserProfile = () => {
// atomの値がPromiseの場合、解決するまで自動的にサスペンドする
const user = useAtomValue(userAtom);
return <div>{user.name}</div>;
};
```
#### パラメータ付きクエリ
IDごとのデータ取得には以下の2パターンを使い分ける。
1. **パラメータ依存atom**(単一のパラメータのみ扱う場合)
```ts
const userIdAtom = atom<string | null>(null);
const userAtom = atom(async (get) => {
const id = get(userIdAtom);
if (!id) return null;
return fetchUser(id);
});
```
2. **Atom Family**(複数のパラメータを同時に扱う、キャッシュが必要な場合)
```ts
import { atomFamily } from 'jotai/utils';
const userAtomFamily = atomFamily((id: string) =>
atom(async () => fetchUser(id))
);
const UserProfile = ({ id }) => {
const user = useAtomValue(userAtomFamily(id));
// ...
};
```
### 3. 再読み込みとUIバージョニング
データの再取得(Refetch)は「手続き的な再実行」ではなく、「UIバージョン(キー)の更新によるステートの再評価」として実装する。これは「データ取得もUIの計算の一部」という宣言的UIの思想に基づく。
#### createReloadableAtom パターン
```ts
import { atom, type Getter } from "jotai";
function createReloadableAtom<T>(getter: (get: Getter) => T) {
const refetchKeyAtom = at