👻

React状態管理ライブラリ Jotai の Atoms in atom ~memo化不要の再レンダリング対処~ #JotaiFriends

2022/01/21に公開

Jotai入門がまだの方はこちらからぜひ!
https://zenn.dev/tell_y/articles/7a5bd147d34ec2

はじめに

公式ドキュメントには、JotaiはReactの再レンダリング問題を解決するために生まれた(参照)、とありましたが実は再レンダリングを抑える使い方についての話題は今回が初めてです。(jotai紹介特集の中で)

3つ、サンプルコードを用意してみました。
元ネタは公式からです。数値が配列で持たせてあり、各要素をCounterで描画し値をカウントアップ(+1ボタン)できるようにします。おまけとして元の配列に要素を追加できるボタンが下部に用意してあります(Addボタン)。

初心者らしく素直に書く

まずはJotai初心者らしく、useState([1, 2, 3])を扱う様に書いてみます。

ぱっと見た感じ問題ないです。
では、どのコンポーネントでレンダリングが走っているか、Math.random()を使って確認してみます。+1を押してみてください。

+1を押した<Counter />以外の無関係のコンポーネントでもレンダリングが起きています。これはuseState()でも同じことが起こります。useCallback使ってmemo化すれば再レンダリングは防げます。
以下のcodesandboxは参考までに。

はい、ではJotai流ではどうすればよいでしょうか。

Atoms in atomで書く

なんと、atom()にはatom()が渡せます。なので以下のように書くことが出来ます。

const countsAtom = atom([atom(1), atom(2), atom(3)]);

<Counter>に渡すpropsはatom()になります。カウントアップするにはCounter内でuseAtom()を使って操作すればよいです。
+1を押してみてください。

いいですね👏 他のコンポーネントでレンダリングは起きていません。
変数名はcountAtomsAtomのようにするといいかもしれません。
ドキュメントでもアドバイスされていますが、Atoms in atomを書きはじめると変数の型が複雑になっていきます。混乱を避けるためにもTypeScriptで書くことをおすすめします。

splitAtom()を使う

別の方法もあります。
atom([1, 2, 3])からDerived atomとしてatoms in atomを定義することが出来ます。utilsからsplitAtom()が提供されているのでこれを使います。

const countsAtom = atom([1, 2, 3]);
const countAtomsAtom = splitAtom(countsAtom);

あとは上記と同じです。+1を押してみてください。

👍

要素を追加するには派生元のcountsAtomを操作します。

まとめ

JotaiはシンプルなインターフェースでuseState()の様に書くことが出来ます。しかし、最大限の恩恵を得るにはJotai流な書き方知り、使う必要があります。
こと再レンダリングの抑制については、Atoms in atomを使いこなせれば確実に恩恵が得られますので覚えておいて損はないかと思います。
今回の記事では簡単な配列を扱いましたが、巨大なオブジェクトも扱えるようにfocusAtomselectAtomなどのutilsも提供されていますので、気になった方は公式ドキュメントを参照ください。

余談ですが、要素を追加するAddボタンを押すと配列が書き換わるため全てのレンダリングが走ります。これも抑えたければCounterのmemo化は必要ですが、パフォーマンスに貢献することは稀かと思います。問題になったらmemo化する、程度に構えればよいでしょう。

Jotaiの紹介特集について

この記事はJotai FriendsによるJotai紹介特集記事の1つです。記事一覧はこちらからどうぞ。

Jotai Friendsとは

いちJotaiファンとして、エンジニアの皆さんにもっとJotaiを知ってもらって使ってもらいたい、そんな思いから立ち上げたのがJotai Friendsです。

https://jotaifriends.dev/

現在まだまだ準備中ですが今後ともよろしくお願いします!
(ご興味持っていただけた方は是非jotaifriends.devにてEメールアドレスのご登録をお願いします🙏)

Jotai Friends

Discussion