Jotai のステートってどこに定義するべき?
普段 React でアプリ開発をしており、Jotai ライブラリを用いています。
Jotai では atom
を用いて、グローバルな状態管理を始めとした色々なことができます。
( Jotai そのものについての詳細は公式サイトや他記事を参考にしてください 🙏 )
const countAtom = atom(0);
さて早速ですが本題です。☝️この定義、どのディレクトリにあるどのファイルに定義すればよいのでしょうか?
私はなんとなく、 atom
定義専用のディレクトリを作りたいです……この気持ちは一体何なんでしょう。これを掘り下げて自分なりに納得した記録です。📝
用途から考える
(万人が必ず当てはまるわけでは無いと思いますが…)
我々の Jotai の使い方は、用途別に大きく3つに分けられます。
- 状態をグローバルに保持するため
- 外部からのデータ取得を容易にするため
- 外部から取得したデータをキャッシュするため
1. 状態をグローバルに保持するため
この用途で使われる atom
は読み取り/更新するために、コード上のあらゆる場所で参照される値です。
意図しない場所で変更されててアプリ全体の挙動が壊れた‼️などが無いように、「グローバルな値である」ということが分かりやすくなっているべきです。
よって、定義する場所から「グローバルな値である」ことをアピールしておきたいです。
📂 src
┣ 📁 app
┣ 📁 hooks
┣ 📁 navigators
┣ 📁 screens
┗ 📂 globalStateAtoms
┣ 📄 countAtom.ts
┗ 📄 settingAtom.ts
2. 外部からのデータ取得を容易にするため
これはデータベースからの読み取り専用値などで、主に atomWithObservable
を用いてリアルタイムに変更が反映されてほしいような値です。
この atom
は基本的にはグローバルに参照されるものではないです。
const historiesAtom = atomWithObservable(() => DBにアクセスするObservableを返す関数);
このような atom
を扱う場合は、基本的にカスタムフックを定義します。🌟
(※規模やルールや好みにも依ると思います…)
export const useHistories = () => {
const histories = useAtomValue(historiesAtom);
return histories;
};
コード上のあちこちで呼び出されるのは useHistories
であり、 historiesAtom
は useHistories
の中でしか呼ばれません。
となると、この atom
はカスタムフックと同じファイルにローカルとして定義すれば充分そうです。
3. 外部から取得したデータをキャッシュするため
これはある程度の時間は更新しなくてもよいプラットフォームからの値などで、 atomFamily
を用いて同じ引数であれば外部通信せずにキャッシュを返したい値です。
const maximumTemperatureAtomFamily = atomFamily(
(param: Param) => atom(async () => fetchMaximumTemperature(param)),
(a: Param, b: Param) => a.year === b.year && a.month === b.month && a.day === b.day
);
// 1時間経ったらキャッシュを無効化
maximumTemperature.setShouldRemove(
(createdAt: number) => createdAt < subHours(Date.now(), 1)
);
この場合も2.と同様、基本的にはカスタムフックを定義します。🌟
export const useMaximumTemperature(param: Param) => {
const maximumTemperature = useAtomValue(maximumTemperatureAtomFamily(param));
return maximumTemperature;
};
2.と同じく、この atom
もカスタムフックと同じファイルにローカルとして定義すれば充分そうです。
📂 src
┣ 📁 app
┣ 📂 hooks
┣ 📄 useHistories.ts 👈️ この中で atom 定義&利用
┗ 📄 useMaximumTemperature.ts 👈️ この中で atom 定義&利用
┣ 📁 navigators
┣ 📁 screens
┗ 📂 globalStateAtoms
┣ 📄 countAtom.ts
┗ 📄 userAtom.ts
まとめ
atom
を定義するディレクトリを分けたい理由は、ファイルのある階層から「変更には他の場所にも影響あるかも ⚠️ 」の注意書き看板を立てたいという気持ちが私は大きかったです。
- グローバル変数の使い方をする
atom
は専用ディレクトリで定義。 - そうでなければ必要になる場所でローカル定義。
後者なんだけど、1つの atom
を複数 hooks で使いたいんだよネ… 🤔
とかの場合は検討の余地アリです。
💬 よいディレクトリ構成があったらぜひ教えてください 👐
余談
🌟 なぜカスタムフックを経由するのか
データ取得方法を隠蔽するため。
💭 このデータを保持してるのはatomなのか、atomFamilyなのか、atomWithObservableなのか…
カスタムフックは☝️これを隠蔽するインタフェースの役割を担っています。
(あとカスタムフックを介したほうが単体テストも実装しやすいし‼️)
Discussion