React Queryを使った開発
最近、React QueryやSWRといった、キャッシュ機構をもったHooksを使う開発が多くなっています。
自分も、ReactのプロジェクトでReact Queryを採用しました。その過程で迷ったことなどのメモです。
React Query vs SWR
React QueryとSWRは似たライブラリです。 自分もどちらを採用しようか迷ったタイミングで、調べてみました。
参考
ただ、調べてみても、できることは両者大きな違いが無さそうでした。
最終的には、React QueryにはDevToolsがあり、SWRには無い。という点でReact Queryを選択しました。 DevToolsでは、どんなキーでどんなデータがキャッシュされているのか確認することができます。
React Queryについて
React Queryの何が良いのかについて触れておきます。
Reactでの開発によくある実装として、以下のようなものがあります
- データを保持するstateを定義する
- useEffectでAPIデータ呼び出しを行う
- API呼び出し中はローディングを表示する
- API呼び出しが終わったら、ローディングを終了して、結果をstateに更新する
- 更新されたstateをもとにコンポーネントを表示する
コードで表すと、以下のようになります。
const App = () => { const [data, setData] = useState(null); useEffect(() => { fetchData().then((res) => setData(res)); }, []); if (!data) { return <Loading />; } return <MyComponent data={data} />; };
もし、Reduxを使っていた場合は、Storeに取得したデータを格納します。
ここで、APIのレスポンスを格納するためのStateやStoreを用意するのではなく、APIのレスポンスをキャッシュすればコンポーネントがStateを定義しなくて良いよね。というアプローチをReact Queryが提供してくれます。
React Queryを使った場合は以下のようになります。
const App = () => { const { data } = useQuery("fetch-data-key", fetchData); if (!data) { return <Loading />; } return <MyComponent data={data} />; };
useQuery
というHooksにキャッシュのキーとデータフェッチ用の関数を渡します。そうすると、fetchData
の結果をキャッシュします。
すると、Appコンポーネントが再レンダリングされる際はキャッシュされたデータをHooksが返します。
もしキャッシュが無いと、Appコンポーネントの再レンダリングごとにAPI呼び出しが発生しそうに見えますが、キャッシュのおかげでそのような問題もありません。
APIからデータを取得して、それを表示するのがメインのアプリケーションであれば、Reduxなどは使わずにReact Queryだけで十分に感じました。
自分の場合は、管理画面を開発したので、このユースケースにとてもマッチしており、シンプルなコードで開発を進めることができました。
React Queryの導入
ここからは React Queryの導入について説明します。
まずはパッケージをインストールします。
$ npm i react-query
次は、QueryClientProvider
を設定します。また、DevToolsを利用する場合はReactQueryDevtools
を配置します。
QueryClientProvider
に渡しているQueryClient
オブジェクトには、細かい設定を行えます。
例えば、どれくらいの期間はキャッシュから返すのか?など。
import { QueryClient, QueryClientProvider } from "react-query"; import { ReactQueryDevtools } from "react-query/devtools"; const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 1 * 60 * 1000, // 1分間はキャッシュから返す refetchOnWindowFocus: false, // ウィンドウにフォーカスしたタイミングでデータを再取得しないように }, }, }); const App = () => { return ( <QueryClientProvider client={queryClient}> <ReactQueryDevtools initialIsOpen={false} /> <MyComponent /> </QueryClientProvider> ); };
あとは、各コンポーネントでuseQuery
といったHooksを利用するだけです。
useQuery
最後に、主に利用しているuseQuery
について説明します。他にもいくつかHooksが用意されていますが、自分は使わなかったので触れません。
useQuery
の第1引数には、キャッシュのキーを指定します。このキーが同じ場合は、キャッシュからデータが返されるので、予期せず違うAPI呼び出しなのに同じキーを指定しないようにしましょう。
第2引数には、Promiseを返す関数を指定します。自分はaxiosと組み合わせて利用しました。
Query Cancellation | React Query | TanStack
const App = () => { const { data } = useQuery("fetch-data-key", fetchData); if (!data) { return <Loading />; } return <MyComponent data={data} />; };
第1引数には配列を指定することも可能なので、ページネーションがあるような場合に便利です。
const { data } = useQuery(["fetch-data-key", pageIndex], () => fetchData(pageIndex) );
任意のタイミングでデータを再取得したい場合のために、refetch
関数も用意されています。
const App = () => { const { data, refetch } = useQuery("fetch-data-key", fetchData); if (!data) { return <Loading />; } return <MyComponent data={data} refetchData={refetch} />; };
staleTime
デフォルトでは、staleTime
が0になっているので、キャッシュを使わずに毎回APIからデータを取得します。
必要に応じて、どれくらいの時間キャッシュから返すのかを指定することになります。