文理のこうさてん

人文科学と自然科学のこうさてんを目指していきたい。

状態管理に関しての知識整理

状態管理について、概念的にまとめた自分用メモ。

状態管理とは

コンポーネント間共通で管理したいデータを扱う手法のこと。  

状態管理の手法

useStateで管理してprops経由でバケツリレーをする

基本的な状態管理方法であるuseStateを用いる手法。複数感コンポーネントで状態をシェアしたい場合は、propsによるバケツリレーを行わないといけない。パフォーマンス観点から再レンダリングをコントロールしにくいという観点と、また責務の観点(単一責任の原則)から好まれる方法ではない。

useContextを使う

useContextは、親から子に状態を渡す場合に限って、propsバケツリレーを回避することができる。 そういえばflutterでアプリ開発をしていた時はChangenotifier&providerパターンで状態を管理していた。

propsによるバケツリレーの心配は無くなったが、ステートが複雑になった場合の管理が煩雑になる可能性がある。例えば、userに関する情報とquestionに関する情報を管理したいとなった時に、providerを二つ用意せねばならない(管理したい対象に応じて、n個ずつproviderが増えていく』)。代替案として、複数のproviderを用いずに単一のproviderを用いるという方法もあるが、パフォーマンスの低下が代償となる。

状態管理ライブラリを導入する

ステート管理のライブラリを導入する方法。代表的なものと、業務で使用しているものを整理する。

react-redux

一つのstoreにまとめて状態を管理する。 selecter経由でstoreから値を取得し、action経由でstoreに対して変更をdispatchする。storeが更新されたとしても、selecterで取得される値に変化がなければ、storeに接続しているコンポーネントは再レンダリングされない。また、useDispatchを使うことで、特定のコンポーネントが更新のみに責務を持つことができ、再レンダリングのコントロールをすることができる。 uhyoさんによると特有の点は以下。

React-reduxに特有の点としては、(グローバルに共有される)すべてのステートを一つのStoreで管理し、selectorによってそこから必要なものだけを取り出すというインターフェースが特徴となります。特に、selectorの型が(state: 全てのステート) => 必要なステートのような型となり、ステートの使用者は常に全体のステートを意識させられます(適宜カスタムフックを用意することでこの点はある程度緩和できますが)。

recoil

reduxをは異なるインターフェースを持った新興のステート管理ライブラリ。状態を管理するAtomと状態を取り出すSelectorのRecoilStateから成り立つ。 全てのステートが一箇所にまとまっていないのが特徴。reduxと異なり、statoのcode splittingが成立する。 uhyoさんまとめは以下。個人的に、良くも悪くもアーキテクチャを最初に固めないといけないので、ある程度経験がある人がチームにいる時に採用したほうがいい気もする。

まとめると、RecoilはAtom・Selectorというシンプルな概念をベースとし、特定のアーキテクチャを強制することなくグローバルなステート管理の部品を提供してくれる点が特徴です。subscriptionの管理などの面倒な部分をRecoilに任せつつ、思いのままのアーキテクチャでステート管理を行うことができるでしょう。

apollo client

今本業で使っている。graphqlサーバーからのdata fetchingだけでなく、cache機構も備えており、状態管理を実現することもできる。 apollo client経由でqueryを叩く際、初回のリクエストはgraphql serverに問い合わせるが、2回目以降はcacheに対して問い合わせるようになる。またcacheの値以外を管理したい場合、Reactive valiablesを用いれば、追加で値を管理することができる。

個人的には、クエリの結果と+アルファを管理したい場合には、apollo clientのcache機構をそのままステート管理に用いていいと思うが、呼び出し時に特別な処理や副作用を起こしたい場合、apollo clientで対処するのは難儀なので、何かしらの状態管理ライブラリに寄せていったほうがいいと思う。

uhyoさんの考察。

これらのライブラリはキャッシュされたデータを識別するための「キー」という概念を持ち、データは末端のコンポーネントが好き勝手に追加できることから、ステート管理のアーキテクチャとしてはRecoilに近いものです。一方で、これらのライブラリは使い勝手を第一に置いたハイレベルなAPIに重きを置いており、ローレベルなAPIを中心とするRecoilとは対照的です。 これらのライブラリによるステート管理は自己完結的5であり、もしデータフェッチングとは関係ないステートも多くある場合、ふたつのステート管理を融合させた状態で最適なパフォーマンスを得るのは難しいと思われます。ここに最適なパフォーマンスが必要な場合は、React QueryやuseSWRなどを使わずにRecoilなどに寄せる選択肢も考える余地があるでしょう。

まとめ

頭の中が整理できてよかった。現状apollo clientのcache機構を本業で、useContextによる管理を副業で使っているが、状況に応じて最適な状態管理をしていきたい。

参考記事

blog.uhy.ooo

blog.uhy.ooo

www.apollographql.com

react-query.tanstack.com