Jōtaiでクライアントのステートをイイジョータイにしタイ
今見えているもの、これはJōtaiによるステート管理です。というより、私達のグローバルステートをより簡単に管理できるその例が表現されているものです。この記事の終わりまでにわかるようになります。
useState
素晴らしい Reactはとても素晴らしく、Reactそのものでとてもシンプルなコンポーネントのステートを管理するものが提供されています。それは useState
というもので、ユーザーのブラウザ上で一時的にシンプルなデータをシンプルなコンポーネントで管理するのにとても向いているものです。皆様も簡単に理解できるよう、具体的な実装を観察してみましょう。
この例では、それぞれの <VeryUsefulComponent />
で count
が管理されており、 setCount
の引数に数を渡すことにより、 count
が更新されます。この記法ではそれぞれ<VeryUsefulComponent />
同士では値が独立して管理されていますね。
Component内で変化する値の管理はこのような方法で簡単に実装できるのが useState
のとても良いところであり、そして難しいところでもあります。さてどのような状況が難しそうでしょうか?ちょっと考えてみましょう。
複数のComponentでステートを管理するむずかしさ
例えば、こんな状況はどうでしょうか?あるコンポーネントは子のコンポーネントで変化する値を取りたいですし、そのコンポーネントでも値を変化させたい、みたいな。皆さんならどう実装しますか?うーん難しいですね。ちょっと手元で実装をしながら考えてみてください。
useState
を使ってやるならばこのような実装ができるかもしれません。
ちゃんと動いてそうですね。一見素晴らしそうなこの実装も実は弱点があります。例えばこの渡したいStateが増えたら大変ですよね。もしくはより入れ子構造になったりしたらこのStateを子ComponentのPropsとして引き回さなければならないのでとても大変です(Prop Drilling
とか言ったりしますね)。
useContext
で値を引き回す
Reactのコミッターもこれはわかっていて、スマートな対応を示しています。 useContext
というReact hookを使った方法を見てみましょう。
<VeryUsefulContext.Provider />
配下のComponentで VeryUsefulContext
を呼び出すことで、 <VeryUsefulContext.Provider />
で渡した値を子コンポーネントで引き回すことができ、子コンポーネントではPropsにその値を入れる必要がなくなっているので、拡張が容易になったことがわかります。子コンポーネントでも、親コンポーネントでも、この値を操作できるのでとても楽そうです。
でもこれはあるツリーの一部の配下のみで値を引き回すことができることや、ContextProviderを挿入することで、ツリーが一つ深くなることに対する抵抗感、このContextがどのような値を持っているかはProviderにいれるときに決定される不安感(ある程度は軽減できるとは思いますが)が私にはありました。
もっとシンプルに、まるで useState
を使うときのような使い心地はないのでしょうか?
Jōtai で気軽にコンポーネント間で値を引き回す
このような課題感を解決するのにJōtaiはとても良い選択肢です。Jōtaiを説明する前に先程のコンポーネントをJōtaiで書き換えてお見せしたほうがいいでしょう。
Componentの構造はほぼ useState
と同じなのがわかると思います。 veryUsefulCountAtom
がこのウィンドウ上のグローバルなステートとして各コンポーネントで共有されており、それを親も、子も、並列している要素でもこれを操作することができましたね。
Jōtaiはグローバルなステート管理としてはとても軽量で、シンプルな利用の体験を提供してくれます。atom
でステートを宣言・初期値を設定し、 useAtom
でそのステートの読み取り・更新を処理します。シンプルに使うならこれらの値はすべての useState
の場所で共通の値が利用され、コンポーネントの親子を超えた関係にもその値の共有・操作を提供します。もちろん、この共有は適切に境界をもたせることで、よりコンポーネントの可用性を向上させることが出来ます。ここからはいくつかのユースケースを検討してみましょう。
複数の状態をもたせる
グローバルステートを持つのに便利なJōtaiですが、もちろんそのステートを区切ることも出来ます。 <Provider>
でその領域を囲うことでそれを実現できます。値を取らないContextのような動きで、とてもシンプルですね。
状態の中に別の状態をもたせる
<Provider>
で囲うことで状態を区切ることが出来ますが、別の状態をこの中に持たせたいと思うこともあるかもしれません。その場合も考慮されており、 Store
を利用することで実現します。
useAtom
にStoreを渡すことで、特定のStoreの状態を共有することが出来ました。これらはProviderの内外問わず指定されたStoreの中で管理されるようになるので、コンテキストに依存しないステートを持ちたい時はとても使える手法なのかもしれません。
まとめ
もう一度最初の例を見てみましょう、あなたはこの一見複雑そうな状態管理が、実はとてもエレガントで、そしてシンプルに記述されていることに気づき、Jōtaiでイイジョータイを保ちたくなったかもしれませんね。
(※この記事は 3Blue1BrownJapan を見ながら書かれました。)
Discussion