react-server675fbba4
react-serverfilesdocssrcpagesja(pages)integrationsreact-query.mdx
docs/src/pages/ja/(pages)/integrations/react-query.mdxmdx7.4 KiB7ac3ae6a

title: TanStack Query category: Integrations order: 4

import Link from "../../../../components/Link.jsx";

TanStack Query

@lazarv/react-serverは、TanStack Query(旧React Query)と連携して、サーバーサイドプリフェッチとクライアントサイドハイドレーションによる強力なデータフェッチを提供します。サーバーコンポーネントでクエリをプリフェッチし、クライアントでシームレスにデータをハイドレートすることで、不要な再フェッチを回避できます。

<Link name="installation"> ## インストール </Link>

TanStack Queryをプロジェクトにインストールします:

pnpm add @tanstack/react-query
<Link name="setup"> ## セットアップ </Link>

TanStack Queryを使用するには、QueryClientを作成し、アプリをQueryClientProviderでラップする必要があります。QueryClientProviderはReactコンテキストに依存するため、クライアントコンポーネントである必要があります。

サーバーとブラウザの両方の環境を処理するクエリクライアントファクトリを作成します:

import {
  defaultShouldDehydrateQuery,
  isServer,
  QueryClient,
} from "@tanstack/react-query";

function makeQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: {
        // SSRでは、クライアントでの即座の再フェッチを避けるために
        // デフォルトのstaleTimeを0より大きく設定します
        staleTime: 60 * 1000,
      },
      dehydrate: {
        // 保留中のクエリをデハイドレーションに含める
        shouldDehydrateQuery: (query) =>
          defaultShouldDehydrateQuery(query) ||
          query.state.status === "pending",
      },
    },
  });
}

let browserQueryClient = undefined;

export function getQueryClient() {
  if (isServer) {
    // サーバー: 常に新しいクエリクライアントを作成
    return makeQueryClient();
  } else {
    // ブラウザ: まだない場合は新しいクエリクライアントを作成
    if (!browserQueryClient) browserQueryClient = makeQueryClient();
    return browserQueryClient;
  }
}

アプリの残りの部分にQueryClientを提供するクライアントコンポーネントを作成します:

"use client";

import {
  isServer,
  QueryClient,
  QueryClientProvider,
} from "@tanstack/react-query";

function makeQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 60 * 1000,
      },
    },
  });
}

let browserQueryClient = undefined;

function getQueryClient() {
  if (isServer) {
    return makeQueryClient();
  } else {
    if (!browserQueryClient) browserQueryClient = makeQueryClient();
    return browserQueryClient;
  }
}

export default function Providers({ children }) {
  const queryClient = getQueryClient();

  return (
    <QueryClientProvider client={queryClient}>
      {children}
    </QueryClientProvider>
  );
}

次に、ルートレイアウトでProvidersコンポーネントでアプリをラップします:

import Providers from "./providers";

export default function RootLayout({ children }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <head />
      <body suppressHydrationWarning>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
<Link name="server-side-prefetching"> ## サーバーサイドプリフェッチ </Link>

@lazarv/react-serverでTanStack Queryを使用する主な利点は、サーバーコンポーネントでデータをプリフェッチし、クライアントでハイドレートできることです。これにより、ローディング状態なしでデータが即座に利用可能になります。

サーバーコンポーネントで、クエリクライアントを使用してデータをプリフェッチし、クライアントコンポーネントをHydrationBoundaryでラップします:

import { dehydrate, HydrationBoundary } from "@tanstack/react-query";

import { getPosts } from "./get-posts";
import { getQueryClient } from "./get-query-client";
import Posts from "./posts";

export default function PostsPage() {
  const queryClient = getQueryClient();

  queryClient.prefetchQuery({
    queryKey: ["posts"],
    queryFn: getPosts,
  });

  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <Posts />
    </HydrationBoundary>
  );
}
<Link name="client-components"> ## クライアントコンポーネント </Link>

クライアントコンポーネントでは、同じクエリキーでuseSuspenseQuery(またはuseQuery)を使用できます。サーバーでデータがプリフェッチされている場合、ローディング状態なしで即座に利用可能になります:

"use client";

import { useSuspenseQuery } from "@tanstack/react-query";
import { getPosts } from "./get-posts";

export default function Posts() {
  const { data } = useSuspenseQuery({
    queryKey: ["posts"],
    queryFn: getPosts,
  });

  return (
    <ul>
      {data.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}
<Link name="isomorphic-data-fetching"> ## 同型データフェッチ </Link>

サーバーとクライアント間でデータフェッチロジックを共有するために、ランタイム環境を検出する同型データフェッチャーを作成できます:

export async function getPosts() {
  if (typeof document === "undefined") {
    // サーバー: データを直接インポート
    const { default: posts } = await import("../data/posts.json");
    return posts;
  } else {
    // クライアント: APIルートからフェッチ
    const res = await fetch("/api/posts");
    return res.json();
  }
}

ファイルシステムルーティングを使用したAPIルートと組み合わせることができます:

import posts from "../../data/posts.json";

export default async function GET() {
  return new Response(JSON.stringify(posts), {
    status: 200,
    headers: {
      "Content-Type": "application/json",
    },
  });
}
<Link name="nested-hydration"> ## ネストされたハイドレーション境界 </Link>

異なるデータセットをプリフェッチするために、別々のサーバーコンポーネントにHydrationBoundaryコンポーネントをネストできます。これは複数のデータ依存関係を構成するのに便利です:

import { dehydrate, HydrationBoundary } from "@tanstack/react-query";

import Comments from "./comments";
import { getComments } from "./get-comments";
import { getQueryClient } from "./get-query-client";

export default function CommentsServerComponent() {
  const queryClient = getQueryClient();

  queryClient.prefetchQuery({
    queryKey: ["comments"],
    queryFn: getComments,
  });

  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <Comments />
    </HydrationBoundary>
  );
}

@lazarv/react-serverでTanStack Queryを使用する完全な例については、TanStack Query exampleを確認してください。