概要
@tanstack/react-query の useQuery を使うと、データフェッチのローディング・エラー・成功状態を宣言的に管理できる。
useState + useEffect による手動管理を置き換え、キャッシュ・リフェッチも自動で扱える。
インストール
npm install @tanstack/react-query
セットアップ(QueryClientProvider)
App Router では QueryClientProvider をクライアントコンポーネントとして用意する。
// src/components/providers/QueryProvider.tsx
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useState } from "react";
export function QueryProvider({ children }: { children: React.ReactNode }) {
const [client] = useState(() => new QueryClient());
return <QueryClientProvider client={client}>{children}</QueryClientProvider>;
}
// src/app/layout.tsx
import { QueryProvider } from "@/components/providers/QueryProvider";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ja">
<body>
<QueryProvider>{children}</QueryProvider>
</body>
</html>
);
}
useQuery でデータフェッチ
// src/components/UserList.tsx
"use client";
import { useQuery } from "@tanstack/react-query";
type User = {
id: number;
name: string;
email: string;
};
async function fetchUsers(): Promise<User[]> {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
if (!res.ok) throw new Error("データの取得に失敗しました");
return res.json();
}
export function UserList() {
const { data, isLoading, isError, error } = useQuery({
queryKey: ["users"],
queryFn: fetchUsers,
});
if (isLoading) return <p>読み込み中...</p>;
if (isError) return <p>エラー: {error.message}</p>;
return (
<ul className="space-y-2">
{data?.map((user) => (
<li key={user.id} className="rounded border px-4 py-2 text-sm">
<span className="font-medium">{user.name}</span>
<span className="ml-2 text-gray-500">{user.email}</span>
</li>
))}
</ul>
);
}
ポイント
queryKeyはキャッシュのキーとして機能する。同じqueryKeyのデータはキャッシュから返されるisLoading/isError/dataで状態を分岐でき、useStateによる管理が不要になるQueryClientをuseStateで初期化することで、リクエストをまたいだインスタンス共有を防ぐQueryProviderは"use client"が必要。Server Component のツリーに直接置けない