フロントエンド開発の状態管理に関してあれこれ整理してみました。
状態管理とは
状態管理(State Management)は、アプリケーション内で「今どういう状態なのか」を記録・操作・共有する仕組みのこと。
例:
- ログインしているユーザー情報
- モーダルが開いているかどうか
- 入力フォームの内容
- APIから取得したデータ
- 現在選択されているタブやページ
など
Storeとは
Store(ストア)は、アプリケーションの状態(state)を一元管理するための仕組み。
VueやReactなどのフロントエンドフレームワークでは、コンポーネント単位で状態を持つことができますが、量が増えて複雑になったり、複数のコンポーネントで共有する必要が出た場合に、Storeが活躍します。
Storeの役割
-
状態の一元管理
コンポーネントごとに状態を持つのではなく、Storeにまとめて管理することで、管理のしやすさが上がり、データの整合性が保たれます。 -
コンポーネント間のデータ共有
親子関係に関係なく、どのコンポーネントからでもStoreの状態にアクセス可能。 -
状態の変更を一元的に制御
状態の変更はStoreのアクションやミューテーションを通じて行うため、変更の流れが明確になる。 -
デバッグ・テストがしやすい
状態の履歴を追いやすく、Vue DevtoolsやRedux DevToolsなどで状態の変化を可視化できる。 -
パフォーマンスの最適化
一度取得したAPIデータをStoreに保持しておけば、再取得せずに使い回せる。
ローカルStore、 グローバルStore
ローカルStoreとグローバルStoreで役割を分ける設計
- ローカルStore
- 特定のコンポーネントやページだけで使う状態
- グローバルStore
- アプリ全体や複数コンポーネントで共有する状態
自分の現場ではページStore(ページ専用のローカルStore)を作り、ページ内だけで使うというルールを設けています。
ただ、ページ内で使うデータが少ない場合や、状態がシンプルな場合は、わざわざページStoreを作らず、コンポーネント内で完結させる事もあり、機能拡張などでStoreが必要になったら作るという方針です。
また、ページ内の各コンポーネントでもレベルごとにStoreへのアクセスを以下の様なルールで制限しています。
| コンポーネント階層 | Storeへのアクセス |
|---|---|
| 上位(Page, Pane) | 操作, 参照可能 |
| 中位(Section, Block等) | 参照のみ |
| 下位(Item, Button等) | 参照不可、Propsで受けるのみ |
これのメリット:
- 状態の流れが明確で、責務がわかりやすい
- 下位コンポーネントが状態を変更できないため、予期しない挙動が起きにくい
- 保守性が高まり、テストしやすい
Storeで管理すべきデータの種類
- ユーザー情報(ログイン状態、プロフィールなど)
- 多くの画面で使うため
- フォーム入力内容(複数ステップのフォームなど)
- 複数コンポーネントで共有する必要がある
- APIから取得したデータ(商品一覧、ユーザー一覧など)
- 一度取得して複数画面で使い回すため
- アプリの状態(ローディング、エラー、通知など)
- グローバルに管理した方が効率的
- 言語設定やテーマなどのUI設定
- 全体で使うため
Storeで管理しない方がいいデータ
- 一時的なローカル状態(モーダルの開閉、入力中の文字など)
- コンポーネント内で完結するため
- ページ固有の状態(スクロール位置、タブの選択など)
- 他のコンポーネントに影響しないため
通信と状態管理の分離
色々な設計パターンがあると思いますが、APIとの通信処理はカスタムhookに切り出し、そのhookをStore経由で呼び、取ってきたデータはStoreで保持するという設計が一般的かと思います。
- カスタムHook:API通信のロジックを担当
- Store:取得したデータの保持・共有・更新を担当
これのメリット:
責務が明確でわかりやすい。保守性が高まり、テストしやすい。
状態管理におけるキャッシュ
状態管理におけるキャッシュとは、一度取得したデータを保存しておき、再利用することで無駄な再取得や再計算を避ける仕組み。
API通信の結果や計算コストの高い状態に対してキャッシュを活用することで、パフォーマンス向上やユーザー体験の改善が期待できる。
Redux、Pinia等でデータマネジメントを担い、以下のキャッシュ専用ライブラリを組み合わせて使うのがいい。
専用ライブラリ
- React:
- React Query や SWR
- Vue:
- Vue Query(React QueryのVue版)
専用ライブラリ以外の手法
- メモ化(Memoization)
- React: useMemo、Vue: computed
- ローカルストレージやIndexedDBに保存
主なStoreライブラリ
- React:
- Redux Toolkit
- Zustand
- Jotai
- Vue:
- Pinia
- VueUse + Reactive/Ref
