Jotai + TanStackQueryのすすめ
はじめに
Reactの状態管理ライブラリはいろいろな選択肢があります。
最近はZustand
がトレンドっぽいですが、僕はJotai
を使うことが多いです。
どちらもdeveloperは同一ですが、設計思想に大きな違いがあります。
ここでは設計思想には言及しませんが、人によって好みは分かれそうな気はします。
今回は、Jotai
のTanStackQuery
インテグレーション機能が実用で使えるレベルになってきたので、紹介していこうと思います。
TanStackQueryって?
旧称はReactQuery
で、Reactのデータフェッチライブラリの1つです。
他に代表的なライブラリでSWR
などがありますが、自分は機能の豊富さでTanStackQuery
をよく使っています。
Jotaiの基礎知識
Jotai
はatom
と呼ばれる最小単位のオブジェクトに値を保持することで状態管理を実現しています。
atom
に保存された値はuseAtom
を使って呼び出すことで、その値を参照することができます。
下記のコード例は、atom
の値を使用する(更新する)カスタムフックを実装した例です。
const countAtom = atom(0);
export const useCount = () => {
const [count, setCount] = useAtom(countAtom);
return {
count,
setCount,
};
}
このカスタムフックを呼び出すことによって、任意のコンポーネント内でatom
に保持された値を共有することが可能です。
もちろんカスタムフックを作らず、直接atom
の値を使いたい場所でuseAtom
することも可能ですが、個人的にatom
はなるべくexport
しないようにするため、カスタムフックを作成することが多いです。
他にもコアな機能はまだまだありますが、今回は割愛させていただいて本題に入りたいと思います。
TanStackQueryインテグレーション
インストール
Jotai
には他ライブラリとのインテグレーション機能が存在します。
今回紹介するのは、TanStackQuery
とのインテグレーション機能です。
まず必要なパッケージをインストールします。
$ npm i jotai jotai-tanstack-query @tanstack/query-core
atomWithQuery
インストールが完了したら、atomWithQuery
という関数が使用可能になっていると思います。
この関数は引数として、TanStackQuery
のuseQuery
と同じものを渡すことができます。
import { atomsWithQuery } from 'jotai-tanstack-query';
import { useAtomValue } from 'jotai';
const [getBooksAtom, getBooksQueryAtom] = atomsWithQuery({
queryKey: ['books'],
queryFn: async () => {
return fetchBooks();
},
});
export const useBooks = () => {
return {
books: useAtomValue(getBooksAtom),
};
};
取得されたデータはgetBooksAtom
に格納されます。
getBooksQueryAtom
にはTanStackQuery
のuseQuery
の返り値が格納されています。
単に取得データのみ扱いたい場合はgetBooksAtom
、isLoading
やerror
などを扱いたい場合はgetBooksQueryAtom
を使用します。
また、getBooksAtom
を使用する場合、データフェッチ中はPromise
をthrow
するためSuspense
を併用する必要があります。
また、フェッチ関数に渡す引数を動的に変えたいときは以下のように書くことができます。
import { atom, useAtomValue, useSetAtom } from 'jotai';
import { atomsWithQuery } from 'jotai-tanstack-query';
const bookIdAtom = atom(0);
const [getBookAtom, getBookQueryAtom] = atomsWithQuery((get) => {
const bookId = get(bookIdAtom);
return {
queryKey: ['book', bookId],
queryFn: fetchBook(bookId),
enabled: !!bookId,
};
});
export const useBooks = () => {
return {
book: useAtomValue(getBookAtom),
setBookId: useSetAtom(bookIdAtom),
};
};
bookIdAtom
の値が変わると、その度にデータフェッチが走ってくれます。
atomsWithMutation
TanStackQueryではデータ取得用のuseQuery
のほかにデータ更新用のuseMutation
も存在します。
もちろんこのインテグレーション機能ではmutate
も使用することが可能です。
mutate
を使用するには、atomsWithMutation
を使用します。
import { atomsWithMutation } from 'jotai-tanstack-query';
const [createBookAtom, createBookMutationAton] = atomsWithMutation((get) => ({
mutationKey: ['createBook'],
mutationFn: async (variables) => {
return createBook(variables);
},
onSuccess() {
get(getBooksQueryAtom).refetch();
},
}));
export const useCreateBook = () => {
const createBookMutation = useAtomValue(createBookMutationAton);
return {
createBook: createBookMutation.mutate,
isCreateBookLoading: createBookMutation.isLoading,
};
};
mutate
の結果がcreateBookAtom
に、TanStackQuery
のuseMutation
の返り値がcreateBookMutationAton
格納されます。
atomsWithQuery
とatomsWithMutation
ともに、それぞれTanStackQuery
のuseQuery
とuseMutation
のオプションや返り値をそのまま使用することができます。
queryClientAtom
インテグレーション機能で使用されるQueryClient
も指定可能なので、TanStackQuery
の便利機能であるdevtools
やQueryClient
に実装されている関数も使用することができます。
1つのQueryClient
インスタンスを共有するには以下のように書きます。
import { QueryClient } from '@tanstack/react-query';
export const queryClient = new QueryClient();
import { queryClientAtom } from 'jotai-tanstack-query';
import { queryClient } from '@/libs/tanstack-query';
export default function App() {
useHydrateAtoms([[queryClientAtom, queryClient]]);
return (
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools />
// children
</QueryClientProvider>
);
}
さいごに
今回は、Jotai
のTanStackQuery
インテグレーション機能について紹介しました。
採用するライブラリとして、Jotai
やTanStackQuery
が選択肢に入るのであれば、一度使ってみてはいかがでしょうか!
Discussion