FFFT

主にwebプロダクト開発に関連する話をつらつらと

SPAの状態管理のパターンとパターン分けの考え方

SPAの状態管理のパターンとパターン分けの考え方についてまとめたいと思います。

SPAの状態管理のパターンとパターン分けの課題

フロントエンドで持つ「状態」には様々なものがあります。

  • 認証情報
  • 検索クエリ
  • APIをコールして取得するサーバサイドで管理されている永続データ
  • フォームの入力情報

などなど。。。


ユーザビリティを考えると様々な種類のデータをフロントエンドで管理する必要が出てきます。
それぞれのデータを一つ一つ見たときにどのように管理すれば良いかという知見は開発をしていく中で感覚的に貯まっていくものだと思います。

例えば認証情報であれば、
「ログインAPIをコールして返却されたセッションIDなどの認証キーをcookieで管理する」
だったり。

例えば検索クエリだったら、
「検索結果をブックマークしたい、または、人に共有したいなどのニーズがあるからURLのクエリストリングで管理する」
だったり。

一方でフロントエンドの状態管理の課題は

感覚的なパターン分けはできるが開発者間で目線を合わせるための共通したパターン分けがない

ここが大きなところかと思います。

少ない人数での開発(2人や3人)であれば相互レビューを通して、お互いで持っている感覚的な状態管理のパターン分けの考え方をすり合わせることもできると思います。
しかし、開発人数がそれ以上増えるとそうは言ってられません。
感覚的なパターン分けに委ねるとそのタイミングで出てきた要件の実現に目線が行って、状態管理の考え方がシステム全体として徐々に多様なものとなっていってしまいます。

気づくとこんなことになる気がします。

👨‍💻「パターン多過ぎ!」
👨‍💻「ここではこう管理してるのにそこではそう管理してるの?」
👨‍💻「もう新しい要件が入る度に都度考えるかー」

となってパターンがどんどん増えていき、検討工数も無駄にかかることになるかと思います。

SPAの状態管理のパターンとパターン分けの課題に対する打ち手

そこでパターン分けの考え方をまとめてくれたのがVictor Savkinさん。

github.com

Victor SavkinさんはAngularのコントリビュータで一番最初に出てくる人です。

f:id:keyama4:20180722162103p:plain

まとめてくれたパターン分けの考え方はこちら。
「Angularにおける」というタイトルになっていますがSPA全般で言える考え方だと思います。

blog.nrwl.io

こちらの記事の「Types of State」が今回のテーマです。

典型的なwebアプリケーションは下記の6つの状態を持つという考え方です。

  • Server state
  • Persistent state
  • The URL and router state
  • Client state
  • Transient client state
  • Local UI state

それぞれ見ていきましょう。

Server state

名前のとおりサーバの状態です。
サーバ上に格納され、SPAであればREST APIを経由してフロントエンドに提供されるデータです。

Persistent state

Persistent stateはクライアント上に展開されるServer stateのサブセット、つまり一部です。
Persistent stateはServer stateのキャッシュとして扱うことができます。
ただ、SPAではより良いUXを提供するためにサーバサイドのデータ更新を待たずにUIを変更するOptimistic Updatesが適用されることがあると思います。
Optimistic Updatesを適用している場合は、完全なServer stateのキャッシュとはなりません。

The URL and router state

URLとルーターの状態です。

Client state

Client stateはクライアントでのみ保持されるデータで、URLで管理されます。
例えば、検索対象のデータはサーバサイドで管理されていますがそれを絞り込むフィルタリングの条件はURLで管理される、というイメージ。

Transient client state

Transient client stateはクライアントでのみ保持されるデータで、URLで管理されません。
ローカルストレージやcookieなどに保持されるデータです。
具体的な例だとyoutubeの動画を途中で停止して改めてその動画ページにくると停止した状態から再生することができます。
URLには停止位置の情報を持っていないですね。

Local UI state

個々のコンポーネントは、動作の状態を管理することができます。
アコーディオンの開閉状態やフォームの入力情報など、コンポーネントの中だけに閉じた状態をLocal UI stateと呼びます。

まとめ

フロントエンドのアプリケーションで考えなくてはならない状態は下記の4つ。

  • Persistent state
  • Client state
  • Transient client state
  • Local UI state

Persistent stateはServer stateと同期されるものでSPAではstoreで管理される状態。
Optimistic Updatesと直接関係する状態。

Client stateはThe URL and router stateと同期されるものでURLで管理される状態。

Transient client stateはローカルストレージやCookieなどで管理される状態。

Local UI stateはコンポーネントのstateで管理される状態。

という感じ。

また、状態を管理する必要が出たときにどのパターンにあてはまるかを迷ったらそのデータのライフサイクルを考えると良さそうです。

たとえばアコーディオンの開閉状態はアコーディオンが表示されているときにのみ必要な情報なのでLocal UI stateと考えることができます。

他にもフォームの入力情報のライフサイクルを考えるとそのフォームが表示されているときにのみ必要な情報なのでLocal UI stateだなと考えることができます。
編集時はAPIをコールしてServer stateからPersistent stateに同期し、Persistent stateから対象のフォームデータを引っ張ってきてフォームのコンポーネントのLocal UI stateに初期値として持たせればそこから先はフォームの入力情報のライフサイクルは新規のときと変わらないものになりますね。

個人的な話ですが有名なライブラリでredux formがありますが、フォームの入力情報をstoreで管理するので上記の考え方からズレるのであまり好きではなかったりします。

開発者間で目線を合わせるための共通したパターン分けにこの考え方を取り入れると共通言語ができて、フロントエンドの状態管理に一貫性を持たせることができるかと思います。
また、状態管理方針の検討工数も減らすことができるのではないでしょうか。