統合このページを編集.md

TanStack Query

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

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

pnpm add @tanstack/react-query

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

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

app/get-query-client.jsx
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を提供するクライアントコンポーネントを作成します:

app/providers.jsx
"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コンポーネントでアプリをラップします:

app/layout.jsx
import Providers from "./providers"; export default function RootLayout({ children }) { return ( <html lang="en" suppressHydrationWarning> <head /> <body suppressHydrationWarning> <Providers>{children}</Providers> </body> </html> ); }

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

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

app/page.jsx
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> ); }

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

app/posts.jsx
"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> ); }

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

app/get-posts.mjs
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ルートと組み合わせることができます:

app/api/GET.posts.jsx
import posts from "../../data/posts.json"; export default async function GET() { return new Response(JSON.stringify(posts), { status: 200, headers: { "Content-Type": "application/json", }, }); }

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

app/comments-server.jsx
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を確認してください。