React状態管理ライブラリ Jotai での別ライブラリ連携 #JotaiFriends
Jotai入門がまだの方はこちらからぜひ!
はじめに
別ライブラリとの連携と聞くと、どういうこと?となるかと思います。ドキュメントのIntegrationsの中を見ると、ImmerやらXState、Reduxなどがあります。まさかReduxなど、別の状態管理ライブラリが登場することに戸惑う方もいることでしょう。
この記事では、なぜJotaiはそれらのライブラリと連携できるのか、その理由は、という点で見て頂ければなと思います。
Immer
ReactとImmerの連携は珍しくもないので驚きは少ないかと思います。(知らない方向け:ImmerはMutableな書き方でImmutableを実現してくれるライブラリです。マサカリ飛んできそうで怖い説明だ)
公式のReact+Immerの紹介はこちらをどうぞ。
atomWithImmer
でatomを作ります。useAtom
から渡されるupdate関数がImmerのproduceを使って渡されるので(c) => (c = c + 1))
とかけます。(src/immer/atomWithImmer)
import { useAtom } from 'jotai'
import { atomWithImmer } from 'jotai/immer'
const countAtom = atomWithImmer(0)
const Controls = () => {
const [, setCount] = useAtom(countAtom)
const inc = () => setCount((c) => (c = c + 1))
return <button onClick={inc}>+1</button>
}
他にもatom(...)
をwithImmer()
でatomWithImmer()
で作ったatomのようにしたり、atom(...)
を使う時にuseImmerAtom
で使ったり出来ます。
Immerの公式ドキュメント
React Query
React Queryの全ての機能をJotaiと共に使える、とのことです。中身を見てみると、atom(new QueryClient())
してました。確かにできそう。
以下はドキュメントに記載されているサンプルです。
import { useAtom } from 'jotai'
import { atomWithQuery } from 'jotai/query'
const idAtom = atom(1)
const userAtom = atomWithQuery((get) => ({
queryKey: ['users', get(idAtom)],
queryFn: async ({ queryKey: [, id] }) => {
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
return res.json()
},
}))
const UserData = () => {
const [data] = useAtom(userAtom)
return <div>{JSON.stringify(data)}</div>
}
<QueryClientProvider>
でwrapする必要も、コンポーネント内でuseQuery()を使う必要も無いようです。全てJotai側が面倒を見てくれるので上記のようにJotaiの世界でReact Queryの恩恵を受けられます。JotaiはKey-lessで使えますが、React QueryがKeyを必要とするのでそこは少し残念ポイントでしょうか。React Queryを使っていればさほど問題はないですね。
React Queryの公式ドキュメント
Redux
さぁ、今回一番のハテナが来ました。Reduxとの連携です。
ここで思い出してみます、JotaiはContextベースで作られておりReactの世界の中で状態を扱います。Reduxはというと、Reactの外に状態を持っています。(そういえば、Reduxのドキュメントの冒頭、Reactが出てこない説明だった気がします。ここです。React学びはじめの頃、戸惑った思い出・・・)
なるほど、Reactの中と外を繋げられるということですね。JotaiとRedux、両方から値を変更することが出来るようです。
以下はドキュメントに記載されているサンプルです。
store.dispatch({ type: 'INC' })
でも変更可能、ということですね。
import { useAtom } from 'jotai'
import { atomWithStore } from 'jotai/redux'
import { createStore } from 'redux'
const initialState = { count: 0 }
const reducer = (state = initialState, action: { type: 'INC' }) => {
if (action.type === 'INC') {
return { ...state, count: state.count + 1 }
}
return state
}
const store = createStore(reducer)
const storeAtom = atomWithStore(store)
const Counter: React.FC = () => {
const [state, dispatch] = useAtom(storeAtom)
return (
<>
count: {state.count}
<button onClick={() => dispatch({ type: 'INC' })}>button</button>
</>
)
}
Reduxの公式ドキュメント
おわりに
いかがでしたか?ハテナの解消に少しでも役立ちましたでしょうか・・・
Immer、React Query、Reduxの3つだけを見てみましたが、各ライブラリの役割をうまくJotaiに組み込んだり連携して実現できていることがわかりました。
Reduxと同じく、Reactの外の世界に状態をもつValtioやZustandも連携できます。その他、XStateは自由すぎるJotaiを整理してくれる役割を担ってくれるでしょう。GraphQLのクライアントライブラリであるURQLとの連携もあります。Apolloはありませんが、コアメンテナーの一人がjotai-apolloを用意してくれています。
連携できるライブラリの多さが、Jotaiをより面白いと思わせてくれます。もし公式が提供していなくても、Jotai with ◯◯ を自作する機会があるかも知れません。その時は既存のライブラリ連携のコードが参考になりますね。
そういえば、この記事で一旦はJotai紹介特集が終わりになります。
何かしらのお役に立っていたら幸いです。Jotai好きになった方、一緒に盛り上げていきましょう!
Jotaiの紹介特集について
この記事はJotai FriendsによるJotai紹介特集記事の1つです。記事一覧はこちらからどうぞ。
Jotai Friendsとは
いちJotaiファンとして、エンジニアの皆さんにもっとJotaiを知ってもらって使ってもらいたい、そんな思いから立ち上げたのがJotai Friendsです。
現在まだまだ準備中ですが今後ともよろしくお願いします!
(ご興味持っていただけた方は是非jotaifriends.devにてEメールアドレスのご登録をお願いします🙏)
Jotai Friends のテックブログです。 React状態管理ライブラリJotai(jotai.org)を新たな選択肢に。 エンジニアの皆さんが安心してJotaiを採用できるように支援します。 Powered by twitter.com/t6adev
Discussion
最近Jotaiを使い始めた身として、Jotai Friendsの皆様の記事には非常に助けられております。
1つ質問なのですが、Redux連携のメリットとしてReact内外の連携を挙げられていましたが、これはJotai v2でStableに追加された
createStore
やgetDefaultStore
などからも同様のことが実現できるのではないでしょうか。こういうケースでは、ReduxとJotaiのStore機能どちらを使うのがいいとお考えですうか?(個人的には、Reducer定義しないといけないのも面倒ですし、必要になったらDependencyAtomを書けばいいし、Jotai単体で完結させたいかなと思っています……)
Jotai v2がこの記事のスコープから外れていることは承知していますが、差し支えなければご意見をお聞かせください。
お役に立てて幸いです!
その通りでして、v2ではReactの外と繋がることが可能になったのでv1時代とは事情は異なっています。
Jotai的にはどうやら正式リリースはしたものの、発展途上でありベストプラクティスは模索中のようです。(主観ですが、アプリケーションレベルでの事例などは見かけないです)
ReduxとJotaiのStore機能どちらを使うのがいいか、ですが、例えば「どうしてもReactの外の世界と繋がる必要があって、そのためにわざわざReduxを導入している状況」であればcreateStoreに置き換えても良いかも知れません。
捉え方としては、本当にstore(= createStore)経由でのatomの操作が妥当なのか、を前提に採用を判断する、でしょうか。そのうえで何か課題や問題等が明確になれば公式へdiscussion/issueを投げるかJotai Friendsへ相談いただければなと思います!
(そろそろv2の記事を出さねばですね・・・)
参考:
1752#discussioncomment-4890441