レビュー待ち·難易度: 中級·更新: 2026-04-16

TanStack Query でデータフェッチとローディング状態を管理する

TanStack Query を使ったデータフェッチ・ローディング・エラー状態管理の実装例。useQuery でシンプルに非同期状態を扱う。

nextjsapistate-managementtanstack-query

対応バージョン

nextjs 15react 19tanstack-query 5

前提環境

React の useState / useEffect の基本を理解していること

概要

@tanstack/react-queryuseQuery を使うと、データフェッチのローディング・エラー・成功状態を宣言的に管理できる。 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 による管理が不要になる
  • QueryClientuseState で初期化することで、リクエストをまたいだインスタンス共有を防ぐ
  • QueryProvider"use client" が必要。Server Component のツリーに直接置けない

注意点

Next.js App Router では QueryClientProvider をクライアントコンポーネントとして設定する必要がある

関連サンプル