state-management について
アプリケーション状態の設計、レビュー、リファクタリングを支援する開発者向けスキルです。最小限の真実の源と有効な遷移を定めてから、ライブラリに対応付けます。
主な機能
- 01ソース、派生、サーバー、URL状態の分類
- 02不変条件と有限状態の定義
- 03イベントと遷移による更新のモデル化
- 04適切な状態配置の選択
- 05一般的な状態ライブラリへの対応付け
活用シーン
- 01新機能の状態設計
- 02重複状態や無効状態のコードレビュー
- 03動作を保った状態のリファクタリング
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ statelyai/ skills --skill state-managementインストール後、Claude Code で/state-managementを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
evals/evals.jsonreferences/async-state.mdno-library.mdreact.mdredux.mdreview-checklist.mdtypescript-modeling.mdvue-pinia.mdzustand.md詳細
状態管理
このスキルは、まず状態のモデリングを行い、その後でライブラリに当てはめる場合に使用します。
目標は、お気に入りのライブラリを推奨することではありません。実装がローカルなコンポーネント状態、リデューサー、Redux、Zustand、Pinia、URL パラメーター、TanStack Query、あるいはライブラリをまったく使わない形になっても、妥当性を保てる状態モデルを作ることです。
担当すること:
- 状態を、妥当な範囲で最小限の信頼できる情報源に絞り込む
- 派生値をセレクター、ゲッター、算出値、またはレンダー時の計算に移す
- モードが互いに排他的な場合は、複数のブール値を明示的な有限状態に置き換える
- 任意のセッター呼び出しではなく、イベントと遷移で状態の変化を定義する
- 各状態を適切な場所に配置する
- 既存のコードをレビューし、不正な状態、重複、同期の不具合、配置が不適切なエフェクトを特定する
- モデルが明確になってから、できあがったモデルをユーザーが選んだライブラリに当てはめる
最初の確認
既存のコードベースでは:
- ローカルの
package.jsonファイルとインポートを確認します。 - 問題の近くにあるコンポーネント、ストア、リデューサー、セレクター/ゲッターのコードを読みます。
- どの状態がローカル、グローバル、リモート、URL に紐づくもの、または外部のものかを特定します。
- 変更を提案する前に、現在の実装の詳細と実際のドメインモデルを切り分けます。
ユーザーがゼロから新しい設計を求めている場合は、まずドメインをモデル化し、その後で実装を選びます。
タスクモード
回答する前に、モードを選択します。
- 設計:要件から新しい状態モデルを構築します。
- レビュー:不具合、リスク、不正な状態、配置が不適切な状態を特定します。
- リファクタリング:動作を維持しながらモデルを簡素化し、不変条件を強化します。
- ライブラリへのマッピング:妥当なモデルを React、Redux、Zustand、Vue/Pinia、またはプレーンな関数に当てはめます。
本当の問題がモデルの破綻である場合に、API の助言へ直行しないでください。
基本分類
配置を決める前に、すべての値を分類します。
- ソース状態:アプリが保存しなければならない、信頼できる事実です。
- 派生状態:ソース状態とプロパティ/入力から計算できる値です。明確な境界上の理由がない限り、保存しないでください。
- 有限状態:
idle、editing、submitting、success、failureのような、互いに排他的なモードです。 - サーバー状態:読み込み、新鮮さ、キャッシュ、無効化への対応が必要なリモートデータです。
- URL 状態:更新後も維持されるべき値、共有に対応すべき値、または戻る/進むナビゲーションに関与すべき値です。
- 一時的な可変値:タイマー、DOM ハンドル、
AbortController、以前のスナップショット、サブスクリプションなど、その他の視覚表示に関わらない可変値です。 - 履歴状態:元に戻す/やり直すスタック、監査ログ、以前の状態、スナップショットです。
値がどの分類にも明確に当てはまらない場合は、その役割をまだ理解できていない可能性があります。
基本原則
- ソース状態は最小限に保ちます。
- それぞれの事実について、信頼できる情報源を一つにします。
- 派生値は同期するのではなく、計算します。
- 互いに排他的なモードは、複数のブール値ではなく、一つの有限な
statusフィールドまたは判別可能なユニオンで表現します。 - 不正な状態を表現しにくくするか、表現できないようにします。
- 更新はドメインイベントと遷移としてモデル化します。
- エフェクトは境界に置きます。エフェクトは状態遷移に反応するものであり、主な調整手段として使うものではありません。
- 状態は、可能な限り低い位置に、必要な限り高い位置に配置します。
- クライアント状態、サーバーキャッシュ、URL 状態を区別します。
- 複数の値が常に一緒に変化する場合は、その関係を明示的にモデル化します。
ワークフロー
ユーザーが明示的に範囲を狭めて依頼しない限り、次の手順を使用します。
- 現在または提案されている状態値をすべて洗い出します。
- 上記の分類体系を使って、各値を分類します。
- 重複を取り除きます。
- ミラーされたプロパティを削除します
- 導出できる値を削除します
- 明確なキャッシュ境界がない限り、オブジェクトとその選択された ID の両方を保持しないようにします
- 不変条件を定義します。
- どの組み合わせが有効ですか?
- どの組み合わせが不可能ですか?
- 各モードで常に成り立つべきことは何ですか?
- 有限モードを特定します。
- 実際には 1 つの状態を表しているブール値は、有限状態にまとめます
- ドメインイベントを列挙します。
- 何が起こり得ますか?
- 誰または何がそれを引き起こしますか?
- イベントにはどのデータが含まれますか?
- 遷移を概略化します。
- 各イベントはどの状態から有効ですか?
- 何が変化しますか?
- 境界でどのような副作用が発生しますか?
- 配置を決めます。
- ローカルコンポーネントの状態
- リデューサー/ストア
- URL
- サーバーキャッシュ/クエリレイヤー
- ref または外部の可変オブジェクト
- モデルを選択したライブラリに対応付けます。
要件が曖昧な場合は、コードを書く前にモデルを示します。
モデリング時の質問
コードを書く前、または見直す前に、次の質問をします。
- 実際に保存する必要がある最小限の事実は何ですか?
- どの値がそれらの事実から導出されるビューですか?
- どのモードが相互に排他的ですか?
- 現在表現可能になっているものの、実際には不可能な状態は何ですか?
- どのようなイベントが起こり得ますか?また、各状態で有効なイベントはどれですか?
- この状態は画面遷移や再読み込みをまたいで保持する必要がありますか?
- これは本当にアプリケーションの状態ですか。それともサーバーキャッシュですか?
- この値はレンダリングに必要ですか。それとも一時的な可変ハンドルですか?
- この状態を所有するのは誰ですか?また、変更できるのは誰ですか?
- ドメインの真実ではなく、実装の詳細を保存していませんか?
- 導出すべき状態を同期するために、エフェクトを使っていませんか?
表現の選択
不変条件を保てる最もシンプルな表現を優先します。
- 独立したローカル値:フィールドが実際に独立している場合に限り、ローカル状態を使います。
- 有限モード:複数のブール値ではなく、文字列リテラルのステータスまたは判別可能なユニオンを使います。
- 協調する更新:複数のフィールドが同時に変化する場合や、イベント処理が現在の状態に依存する場合は、リデューサーを使います。
- 複雑なワークフロー:ガード、再試行、キャンセル、並行処理、子プロセスがある場合は、ステートマシンまたはアクターモデルを使います。
- リレーションを持つコレクション:ネストされた更新が扱いにくくなる、または一貫性を欠く場合は、エンティティを正規化します。
- 導出される読み取り:セレクター、ゲッター、算出値、またはレンダリング時の計算を使います。
- サーバーデータ:リモートのライフサイクルに関する懸念を UI ストアに無理に持ち込まず、クエリ/キャッシュツールを優先します。
- レンダリングに使わない可変データ:ref または外部の可変オブジェクトを使います。
よりシンプルな抽象化では必要な不変条件を保証できない場合を除き、より複雑な抽象化は推奨しないでください。
配置の指針
次の点を確認して、状態をどこに置くか決めます。
- それを読み取るのは誰ですか?
- それを書き込むのは誰ですか?
- どのくらいの期間保持する必要がありますか?
- 再読み込み後も保持する必要がありますか?
- URL で共有できる必要がありますか?
- 権威あるアプリケーションの状態ですか。それともリモートキャッシュですか?
基本的な方針:
- 一時的で表示にのみ使う状態はローカルに保持します
- 複数のコンシューマーが本当に必要とする場合に限り、コンポーネント間で共有するクライアント状態をリデューサー/ストアに保持します
- 共有可能なナビゲーション/フィルタリングの状態は URL に保持します
- 取得したデータはサーバー状態/キャッシュレイヤーに保持します
- 視覚的な表示に使わないハンドルは、レンダリング状態から分離します
アンチパターン
レビューやリファクタリングでは、以下を積極的に指摘してください。
- 派生状態を保存し、エフェクトで再同期している
- propsをローカル状態にミラーリングしている
- 1つのモードを表すブール値が複数ある
- 明確な理由なく
selectedItemとselectedItemIdの両方を保存している - コンポーネント、ストア、URLの間で状態が重複している
- ドメインイベントではなく、セッターを表すアクション名を使っている
- 正規化すべきリレーショナル状態が深くネストしている
- ビジネスロジックを調整するために
useEffectを連鎖させている - 強い理由なく、フォーム状態の大半をグローバルストアに入れている
- 明示的な状態の代わりに、nullableなフィールドを寄せ集めている
- サーバー状態、クライアント状態、リクエストのライフサイクルを、区別のない1つの塊に混在させている
参照先の振り分け
現在のタスクに関連する参照先のみを開いてください。
- React:
references/react.md - ReduxとRedux Toolkit:
references/redux.md - Zustand:
references/zustand.md - VueとPinia:
references/vue-pinia.md - プレーンなJavaScriptまたはライブラリに依存しない実装:
references/no-library.md - TypeScriptのモデリングと網羅性:
references/typescript-modeling.md - 非同期ライフサイクルのモデリング:
references/async-state.md - レビューと問題の検出:
references/review-checklist.md
コードベースでXStateやアクターシステムをすでに使用しており、問題が特にそれらに関する場合は、実装の詳細には専用のxstate-v5スキルを優先してください。
出力形式
次の回答形式を優先してください。
- 状態の一覧:現在存在するもの、または必要なもの。
- 問題点または不変条件:重複、不可能な状態、設計上の制約。
- 推奨モデル:
- ソース状態
- 派生状態
- 有限状態または判別可能なユニオン
- イベントと遷移
- 配置場所
- ライブラリへの対応付け:ユーザーが特定のスタックでの実装を求めた場合のみ。
- コード:モデルが十分明確になってから。
レビューでは、具体的な問題を最初に示してください。設計タスクでは、モデルを最初に示してください。
最終セルフチェック
回答する前に、以下を確認してください。
- 保存されているすべての値が本当に保存する必要のあるものか
- 派生値がソース状態としてミラーリングされていないか
- 相互排他的なモードが、互いに無関係なブール値で表現されていないか
- 無効な状態が削減または排除されているか
- 状態の所有者と配置場所が明確か
- セレクターや遷移の役割をエフェクトに担わせていないか
- 非同期のステータスが明示的にモデル化されているか
- 推奨する抽象化が、問題に対して複雑すぎないか
- ライブラリ固有の助言が、ユーザーの実際のスタックに合っているか