state-management について
state-managementは、フロントエンドの状態をどこで管理するか決めるチーム向けのスキルです。ライフサイクルごとに分類し、適切な最小の管理先を提案します。ライブラリ選びから始めず、陥りやすい誤った管理先も示します。
主な機能
- 01ライフサイクル別の状態分類
- 02最小限で適切な管理先の選定
- 03URL、フォーム、サーバー、クライアント状態の分離
- 04必要に応じたクライアントストア比較
- 05よくある管理上のアンチパターンの指摘
- 06関連作業を他のスキルへ振り分け
活用シーン
- 01共有UI状態の管理先の選定
- 02フィルターやページネーションの保存先の判断
- 03取得データとクライアントワークフローの分離
- 04状態の不具合をデバッグへ振り分け
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ akillness/ jeo-skills --skill state-managementインストール後、Claude Code で/state-managementを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
SKILL.toonevals/evals.jsonreferences/decision-matrix.mdhandoff-boundaries.mdownership-packets-and-route-outs.md詳細
状態管理
実際の問いが次のような場合に、このスキルを使用します。
この状態はどのパケットに属し、誰が所有すべきで、魅力的に見えるものの避けるべき誤った所有者は何でしょうか?
ライブラリの比較から始めないでください。まず主要なパケットを1つ特定し、そのライフサイクルに合う最小限の所有者を選びます。
複数のパケットが混在するリクエストや曖昧なリクエストに対応する前に、references/ownership-packets-and-route-outs.md を読んでください。
パケットが明確で、候補となる所有者を比較する必要がある場合は、references/decision-matrix.md を読んでください。
リクエストが実際には react-best-practices、api-design、debugging、design-system、design-system、または responsive-design の対象である可能性がある場合は、references/handoff-boundaries.md を読んでください。
このスキルを使用する場面
- フロントエンドの問題が、ローカル UI 状態、サブツリー共有状態、URL/ナビゲーション状態、フォーム状態、サーバー状態、長期的なクライアントワークフロー状態のどれに当たるかを判断します。
- 「状態管理が必要」という曖昧なリクエストを、具体的な所有者の推奨事項に落とし込みます。
- Context、Zustand、Redux Toolkit、Jotai、TanStack Query、ルーターによるネイティブな所有の中から選択し、それらを同じものとして扱わないようにします。
- 「すべてを1つのストアで管理する」という提案を、実装開始前に止めます。
- 実装やレビューのために、簡潔な所有権の概要を作成します。
このスキルを使用しない場面
- 主な課題が再レンダリングの頻発、ハイドレーションの不一致、ウォーターフォール、またはクライアント/サーバーのパフォーマンス挙動である →
react-best-practicesを使用します。 - 主な課題がミューテーションの形状、バックエンドの所有権、または楽観的書き込みの API 設計である →
api-designを使用します。 - 主な課題が古いクロージャ、競合、壊れた楽観的更新、または既存の状態バグのデバッグである →
debuggingを使用します。 - 主な課題が制御/非制御コンポーネント API、プリミティブ、スロット、または再利用可能なコンポーネントの契約である →
design-systemを使用します。 - 主な課題がビジュアルシステムのガバナンスまたは共有設定のポリシーである →
design-systemを使用します。 - 主な課題がビューポートへの適応またはレイアウトの崩れである →
responsive-designを使用します。 - アーキテクチャの選択がすでに済んでおり、ユーザーがコードだけを必要としている → 選定をやり直さず、直接実装します。
手順
ステップ 1:主要なパケットを1つ選ぶ
ツールを挙げる前に、主要なパケットを選びます。
- local-ui — トグル、開いたパネル、インライン編集状態、アクティブな行、1つの機能に限定される一時的な状態
- shared-subtree — テーマ、認証スナップショット、ロケール、機能フラグ、中程度のシェル状態
- url-navigation — フィルター、並び順、ページネーション、選択中のタブ、ディープリンク可能なビュー状態
- form-lifecycle — 変更済み/操作済みの状態、バリデーション、送信ライフサイクル、非同期送信エラー
- server-state — 取得したデータ、キャッシュ、無効化、再検証、楽観的なサーバー更新
- client-workflow — ページをまたぐ下書き、元に戻す操作、複数ステップの調整、オフライン編集、長期的なワークフロー状態
任意:副次的なパケットを1つか2つ挙げてもかまいませんが、主要なパケットは必ず1つに絞ります。
簡単な枠組み:
Primary packet: server-state
Secondary packets: url-navigation, local-ui
Decision pressure: shareable filters + remote list freshness
ステップ 2:最小限の所有者を選ぶ
パケットに合う、最も範囲の狭い所有者を選びます。
- local-ui → ローカル状態、リフトアップした状態、または
useReducer - shared-subtree → 中程度のツリーに共有の真実の情報源が1つ必要な場合は Context
- url-navigation → 更新、ディープリンク、戻る/進むの操作を自然に機能させる必要がある場合は、ルーター/検索パラメーター
- form-lifecycle → アプリ全体のグローバル状態ではなく、フォームレイヤーで所有
- server-state → キャッシュ/再検証のルールが重要な場合は、TanStack Query またはルーターのネイティブなデータ API
- client-workflow → 先行するパケットを切り分けた後にのみ、Zustand、Redux Toolkit、Jotai、または明示的なステートマシンのモデリングを使用
ルール:状態が実際にはルートに基づくもの、またはサーバーに基づくものである場合、既存のストアがあるというだけの理由で汎用的なクライアントストアに隠さないでください。
ステップ 3:アンチパターンを確認する
最もありがちな誤った所有先を、明示的に指摘してください。
- ライフサイクルが異なる無関係な状態を、すべて 1 つのストアにまとめる
- オフラインやワークフロー上の理由がないのに、サーバーキャッシュをクライアントストアに複製する
- 共有可能なフィルターやタブを URL の外に隠す
- フォームの dirty/touched/submit のライフサイクルをグローバルなアプリ状態に押し込む
- UI が遅い、または不具合がある問題をすべて、状態アーキテクチャの問題として扱う
適切な回答では、選択した所有先と、つい選びたくなる誤った所有先の両方を挙げます。
ステップ 4:クライアントワークフローのパケットが残る場合に限り、クライアントストアの選択肢を比較する
ローカル/共有/URL/フォーム/サーバーの各パケットを分けた後で、次のように判断します。
- Context:実際の課題が、中規模のツリー内でのアクセスや配布である場合
- Zustand:複数のコンポーネントやページにまたがるワークフロー状態を、少ない手間で管理する必要がある場合
- Redux Toolkit:ワークフローが密接に結合している、イベントが多い、またはより強い規約と監査可能性が必要な場合
- Jotai:粒度の細かいアトムの合成が、アプリの考え方に本当に合う場合
- ステートマシン/明示的なワークフローモデリング:遷移、ガード、複数ステップの調整が中心となる場合
すでに URL/フォーム/サーバーが所有すべきパケットについて、ストアを比較しないでください。
ステップ 5:複合アーキテクチャを明示する
次のような健全な組み合わせは一般的です。
- URL 状態 + クエリ/ルーターのデータ + ローカル UI 状態
- Context + クエリ/ルーターのデータ
- Zustand + クエリ/ルーターのデータ
- Redux Toolkit + クエリ/ルーターのデータ
- フォーム状態 + URL 状態 + クエリ/ルーターのデータ
1 つの万能ストアを求められた場合は、ライフサイクルの異なる状態をまとめると何が悪化するのか説明してください。
ステップ 6:関連する作業はすぐに適切な場所へ振り分ける
所有先を選んでいる間だけ、ここで対応します。
次の段階が以下に該当する場合は、すぐに振り分けてください。
- 再レンダリング/ハイドレーション/ウォーターフォール/パフォーマンスの問題は
react-best-practices - ミューテーションの契約やバックエンドとクライアントの責任分担は
api-design - すでに壊れている状態の挙動は
debugging - コンポーネント API の所有権は
design-system - システムレベルの設定/ガバナンスルールは
design-system - レイアウト/ビューポートの挙動は
responsive-design
ステップ 7:所有権に関する概要を 1 つ作成する
次の形式を使ってください。
Primary packet:
Secondary packets:
Recommended owners:
- ...
Why this is the smallest viable split:
- ...
Avoid:
- ...
Route-out note:
- ...
Next implementation step:
- ...
簡潔にまとめてください。「何もしないで、状態が存在する場所により近いところに置く」のが正直な答えなら、そう明言してください。
出力形式
簡潔な所有権の推奨事項を、次の内容を含めて返してください。
- 主要なパケットと、あれば副次的なパケット
- パケットごとに選択した所有先
- 理由を述べる 1 段落
- 避けるべきアンチパターンを明示
- 次の段階を別のスキルが担当すべき場合は、振り分け先の注記
例
例 1:ダッシュボードのデータ、フィルター、モーダル
入力:「このダッシュボードのプロジェクトデータ、フィルター、モーダル状態には、Zustand と TanStack Query のどちらを使うべきですか?」
適切な出力例:
- 主要なパケット = サーバー状態
- 副次的なパケット = URL ナビゲーション、ローカル UI
- プロジェクトデータ = TanStack Query またはルーター標準のデータ所有
- フィルター = 共有/ブックマーク可能なら URL/検索パラメーター
- モーダル状態 = ローカル状態。離れたブランチ間の調整が必要な場合に限り、軽量なクライアント状態
- オフラインやワークフロー上の理由がないのに、取得したエンティティを Zustand に複製しない
例 2:テーマと認証のプロップドリリング
入力:「テーマと認証情報が 5 階層にわたって渡されています。Redux が必要ですか?」
適切な出力例:
- 主要なパケット = 共有サブツリー
- まず Context を推奨
- さらに広範で密接に結合したワークフロー状態がない限り、Redux は推奨しない
例 3:フィルターとローダーデータを Zustand に保存する React Router アプリ
入力:「React Router アプリでフィルターとローダーデータを Zustand に保存しています。問題ありませんか?」
適切な出力例:
- 主要なパケット = クライアントワークフローではなく、URL ナビゲーションまたはサーバー状態
- 共有可能なフィルターは URL/検索パラメーターに移す
- 別途キャッシュが本当に必要な場合を除き、ローダー/アクションのデータはルーター標準のデータ所有に任せる
- Zustand は、残ったワークフロー調整にのみ使う
例 4:楽観的更新の不具合
入力:「状態コードにバグがあり、楽観的更新が機能していません。次のステップを担当するスキルはどれですか?」
適切な出力の形式:
- 担当の選択よりも、進行中の障害の診断を優先することを示す
- 次のステップを
debuggingに振り分ける - 楽観的書き込みの契約そのものが本当の争点であれば、
api-designにも言及する
ベストプラクティス
- ツールより先にパケットを選びます。
- 実行可能な範囲で最小の担当を選びます。
- 強い理由がない限り、URL/フォーム/サーバー/クライアントのワークフローのライフサイクルを分けておきます。
- 誤った担当を明示します。
- 複数の担当が関わることを、標準化の失敗ではなく通常のこととして扱います。
- 実装者やレビュアーがすぐに行動に移せる簡潔な概要で締めくくります。