👻

Jotaiは手軽に使える状態管理ツール

2022/10/24に公開

https://parque.io

株式会社パルケの手を動かすCTO、みつるです。

この記事は先日ツイートしたものを加筆修正したものとなります。

https://twitter.com/MitsuruOkura/status/1577782191026622464?s=20&t=mpdaStbowHXC8HNEk_ccJg


少し前まで、Reactの状態管理ツールはZustandがファーストチョイスでした。
今は考えが変わってJotaiがファーストチョイスとなっています。

https://jotai.org/

以前、状態管理ツールの沼にハマって試行錯誤した経緯はこちらの記事に書きました。
https://zenn.dev/mitsuruokura/articles/f6073c61b944ea

この後、状態管理ツールに対する考えに変化があり、今は私にとってJotaiがファーストチョイスとなっています。

状態管理ツールの責務が軽減された

TanStack Queryを積極的に使い始めてから、サーバーと同期するようなトランザクションデータはTanStack Queryのキャッシュ任せになりました。

そうすると、状態管理ツールが担っていた役割がどんどん少なくなっていきました。
結果として残ったのは、コンポーネントのPropsで受け渡しをするには面倒な、小さな状態群となりました。

  • 画面遷移の状態
  • 認証トークン
  • サーバーに送信する前の一時的な情報
  • ユーザー操作の履歴

TanStack Queryでも、わざわざ章を割いてこの部分を解説していました。

https://tanstack.com/query/v4/docs/guides/does-this-replace-client-state

"Is it worth it to keep using our client state manager for this tiny global state?"

完全に無くす事も不可能ではないですが、それでも専用の状態管理ツールはあった方が便利、と思っています。

今私が状態管理ツールに求めること

小さく個別にあつかえること

業務データセットを扱うわけではないので、大きなストアを定義する必要はありません。
一つにまとめるより、個々の独立した情報を必要な単位で扱える方が使い勝手がよいと思います。

コードの記述量が少なく、簡単にすぐ覚えられること

ちょっとしたデータを状態に保存したい、と思った時に「さてどうやるんだっけな」とドキュメントを参照する必要があると、使うのが面倒になってしまいます。

えいやっでコンポーネントのバケツリレーで対応してしまうかもしれません。

思い立ったら、記憶をもとにすぐ書ける、敷居の低さが大事です。

必要に応じてローカルストレージなどに永続化できること

ローカルの画面遷移の状態やユーザーの操作状態など、ローカルの状態はリロード後も復元して欲しいものです。
あまり追加のコードを書かなくても、ローカルストレージなどに同期してくれると非常に嬉しいです。

Reactのコンポーネントツリーの外側からも参照・更新できる

外部ライブラリの使用等で、まれにコンポーネントツリーの外からグローバルにアクセスしたい場合があります。
その場合は、リアクティブである必要はありません。
ケースとしてはレアですが、やろうと思えばできるようになっていると助かります。

Jotaiがファーストチョイスな理由

Jotaiなら私が状態管理ツールに求める事が、高いレベルで実現できます。

ノールックで使える簡単さ

ミニマムだとこれだけで使えてしまいます。
プロバイダーコンポーネントを作ってラップする必要すらありません。

import { atom, useAtom } from 'jotai'

const textAtom = atom('hello')

const Input = () => {
  const [text, setText] = useAtom(textAtom)
  const handleChange = (e) => setText(e.target.value)
  return (
    <input value={text} onChange={handleChange} />
  )
}

APIがシンプルなので、一度書けば忘れません。
文字列でユニークなキーを定義する必要もないので、思考の流れを止めません。

使いたい時に、何も参照しなくても書き切れる簡単さが最高です。

ドキュメントがキレイで充実している

シンプルで直感的に使えるので、残念ながらあまり参照する機会はないのですが、最近公開されたドキュメントページがレイで充実しています。

疑問に思ったことは、ドキュメントを見れば解消します。

さらにドキュメントを眺めていると、便利な使い方やユーティリティが見つかり、利用の幅を広げてくれます。

更新atomでビューとロジックを分離できる

atomを更新するさいの複雑な処理や、他のatomを同期して更新したい、他のatomの値に応じて処理を変えたい、といったロジックを更新atomで実現できます。

この更新atomが作成できることで、多くのロジックをコンポーネントのビューから分離できます。

const readOnlyAtom = atom((get) => get(priceAtom) * 2)
const writeOnlyAtom = atom(
  null, // it's a convention to pass `null` for the first argument
  (get, set, update) => {
    // `update` is any single value we receive for updating this atom
    set(priceAtom, get(priceAtom) - update.discount)
  }
)

更新atomで、atomのゲッター(get)と、atomのセッター(set)が利用できることがポイントです。
これにより、更新atomの中で自在に他のatomの値を取り出したり、更新できます。

簡単にストレージに同期できる

ユーティリティのatomWithStorageを使うだけで、atomの状態をストレージに永続化することができます。

import { useAtom } from 'jotai'
import { atomWithStorage } from 'jotai/utils'

const darkModeAtom = atomWithStorage('darkMode', false)

React Nativeで利用する場合は、AsyncStorageを渡すことができます。
AsyncStorageは、React Native WebではLocal Storageとして振る舞います。

import { atomWithStorage, createJSONStorage } from 'jotai/utils'
import AsyncStorage from '@react-native-async-storage/async-storage'

const storage = createJSONStorage(() => AsyncStorage)
const content = {} // anything JSON serializable
const storedAtom = atomWithStorage('stored-key', content, storage)

レコードセットに対してキーを使って取得、更新できる

ユーティリティのatomFamilyを利用すると、レコードセットに対してキー値で必要なatomだけ取り出したり更新できて便利です。
私はレコードセットをatomであつかう場合、atomFamilyを愛用しています。

これはルームID毎に入力中のメッセージの値を保持しておくatomの例です。

export const roomInputMessageAtomFamily = atomFamily(
  (roomId: string) => atom<string>(''),
  (a, b) => a === b,
);

今後のバージョンアップへさらなる期待

Jotaiは現在アクティブに更新されており、次々と新しい機能がリリースされています。
今後も目が話せません。

unstable_createStoreでコンポーネントの外からアクセス

まだRFCの段階ですが、unstable_createStoreを利用すると、どこからでもatomの情報を取得したり更新できます。

https://github.com/pmndrs/jotai/issues/861

レアケースですが、どうしてもコンポーネントツリーの外側でatomの情報を取り出したり更新したい場合があります。
その時にこれがあると助かります。

次のメジャーバージョンアップでunstableが取れる事を期待しています。

Jotai Signal

jotai-labsというリポジトリでは、さまざまな実験的な機能が試されています。

その中で、Jotai Signalはレンダリングのパフォーマンスを改善させる大きな可能性を秘めています。

Jotai Signalについてはこちらの記事に詳しく書かれています。
https://zenn.dev/dai_shi/articles/01813b22907dcf

最後に

株式会社パルケでは、無料で誰でも簡単に使えるミーティングアプリ、チャットアプリをJotaiをフル活用して構築しました。
興味がありましたらぜひ利用してみてください。

無料でずっと話せるミーティングアプリ パルケミート
とにかく簡単につながる無料ビジネスチャット パルケトーク

2023/2/2追記

Jotai v2への移行記録をアップしました。

https://zenn.dev/mitsuruokura/articles/3a832054bd2a9f

Discussion