環境
- react @18.2.0
- react-query @4.20.4
TanStack Queryとは
まずは、TanStack Queryとは何かです。TanStack Queryは以下のような特徴があります。
- 非同期のstate管理が行える
- server stateの fetching, caching, synchronizing and updatingを簡単に行う事ができる
- ReactのHooksであるuseEffectを使ったデータ取得方法は、clientのstateを扱うのに優れているが、非同期やserverのstateを扱うことができない
- backgroundでデータの更新をしてくれ、データの更新が高速になる
TanStack Queryの使い方
TanStack Queryの使い方を理解する上で、まずは見慣れたuseEffect、useStateを使ったデータfetchの例を見てみましょう!その後にTanStack Queryと比較するとよりわかりやすくなると思います。
実行環境の用意
実行環境を以下の手順で用意します。
- プロジェクトを作成するための任意のディレクトリを作成
- プロジェクト立ち上げ
% yarn create react-app . --template typescript
yarnを使用して、typescriptで作成します。
3. dataをfetchするためにaxiosを使用するためinstallしておきます。
% yarn add axios
4. サンプルデータはJSONPlaceholderを使用します。
以上で準備は完了です!
React Hooksの useEffect, useStateを使ったdata fetch
Hooks.tsxという名前でファイルを作り、以下のように記述します。
import axios from "axios";
import { useEffect, useState } from "react";
type Album = {
userId: number;
id: number;
title: string;
};
export const Hooks = () => {
const [albums, setAlbums] = useState<Album[]>([]);
useEffect(() => {
const fetchAlbums = async () => {
const result = await axios.get<Album[]>(
"https://jsonplaceholder.typicode.com/albums"
);
setAlbums(result.data);
};
fetchAlbums();
}, []);
return (
<div>
<p>Hooks</p>
{albums?.map((album) => (
<p key={album.id}>{album.title}</p>
))}
</div>
);
};
- JSONPlaceholderは、何でも良いですが、ここではAlbumを使用しています。
- albumsというstateを用意し、useEffectで初回レンダリング時にのみdata fetchされるようにし、setAlbumsで更新しています。
上記で作成したHooks componentをApp.tsxに追加して、表示の確認をします。
import "./App.css";
import { Hooks } from "./components/Hooks";
function App() {
return (
<div className="App">
<Hooks />
</div>
);
}
export default App;
データを取得して表示できていることを確認できたかと思います。
Tanstack Queryを使ったdata fetch
Tanstack Queryを使うと、同じデータ取得をどう記述するか見ていきましょう!
まずはindex.tsxで、Appコンポーネントを囲むかたちで設定を記述します。
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient();
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</React.StrictMode>
);
App.tsxではTanstackQueryコンポーネントを呼び出します。
import "./App.css";
import { TanstackQuery } from "./components/TanstackQuery";
function App() {
return (
<div className="App">
<TanstackQuery />
</div>
);
}
コンポーネントはTanstackQueryという名前で作ります。
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
type Album = {
userId: number;
id: number;
title: string;
};
const fetchAlbums = async () => {
const result = await axios.get<Album[]>(
"https://jsonplaceholder.typicode.com/albums"
);
return result.data;
};
export const TanstackQuery = () => {
const { isLoading, error, data } = useQuery<Album[]>({
queryKey: ["album"],
queryFn: fetchAlbums,
});
if (error) return <p>An error has occurred</p>;
if (isLoading) return <p>Loading...</p>;
return (
<div>
<p>Tanstack Query</p>
{data?.map((album) => (
<p key={album.id}>{album.title}</p>
))}
</div>
);
};
useQueryの第一引数には、Query固有のkeyを、第二引数には実行関数を記述します。
このkeyは任意に設定できるもので、何でもよいです。
useQueryは、loadingやerrorの状態も取得することができるため、isLoadingや、errorを使うことができます。
以上が一般的なTanStack Queryの使い方です。
Suspenseを使ったloading状態の表示
次に、Reactの標準ライブラリのSuspenseを使用して、loadingやerrorの状態を表示していきます。
- Suspenseの対象としたいコンポーネントをSuspenseで囲う
- Suspenseにpropsとしてfallbackを渡し、loading中の処理を記述する
- 上記より、useQueryのisLoadingは不要となる
- useQueryの第三引数に、オプションとしてsuspenseを有効化する記述をする
import { Suspense } from "react";
function App() {
return (
<div className="App">
<Suspense fallback={<p>Loading...</p>}>
<TanStackQuery />
</Suspense>
</div>
);
}
Suspenseで囲われたTanStackQueryコンポーネントの全体が、Suspenseの対象となります。
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
type Album = {
userId: number;
id: number;
title: string;
};
const fetchAlbums = async () => {
const result = await axios.get<Album[]>(
"https://jsonplaceholder.typicode.com/albums"
);
return result.data;
};
export const TanStackQuery = () => {
const { isLoading, error, data } = useQuery<Album[]>({
queryKey: ["album"],
queryFn: fetchAlbums,
suspense: true,
});
if (error) return <p>An error has occurred</p>;
// suspenseを使用すると不要となる
// if (isLoading) return <p>Loading...</p>;
return (
<div>
<p>React Query</p>
{data?.map((album) => (
<p key={album.id}>{album.title}</p>
))}
</div>
);
};
TanStack Queryの一般的な使用法と同様に、loading中の表示をすることができました。
上記の方法だと、TanStackQueryコンポーネント内でのみ、suspense: trueの設定となります。
Suspenseのユースケースを考えると、プロジェクト内全体でsuspeseの設定をしたいので、この設定をindex.tsxにしていきます。
- TanStackQuery内のsuspense: trueの設定は削除
- index.tsxに設定を記述
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
suspense: true,
},
},
});
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</React.StrictMode>
);
同様に、Suspenseが有効化されており、loadingが表示されることを確認できました。
Suspenseを使ったerrror状態の表示
上記ではSusupenseを使ったloadingの表示をすることができたので、次はerrorの表示をしていきます。
Suspenseでerrorのハンドリングを行う場合は、追加でreact-error-boundaryというライブラリが必要になるのでinstallします。
% yarn add react-error-boundary
- SusupenseをErrorBoundaryで囲む
- propsとしてfallbackを渡し、error時の処理を記述する
import { Suspense } from "react";
import { ErrorBoundary } from "react-error-boundary";
import "./App.css";
import { TanStackQuery } from "./components/TanStackQuery";
function App() {
return (
<div className="App">
<ErrorBoundary fallback={<p>An error has occurred</p>}>
<Suspense fallback={<p>Loading...</p>}>
<TanStackQuery />
</Suspense>
</ErrorBoundary>
</div>
);
}
export default App;
error時にfallbackに記述した内容が表示されることを確認できました。
以上、お読みいただきありがとうございました。
コメント