データローディング
はじめに
データは、loader と clientLoader からルートコンポーネントに提供されます。
ローダーデータは、ローダーから自動的にシリアライズされ、コンポーネントでデシリアライズされます。文字列や数値のようなプリミティブ値に加えて、ローダーは Promise、Map、Set、Date などを返すことができます。
loaderData プロップの型は、自動的に生成されます。
クライアントデータローディング
clientLoader は、クライアントでデータをフェッチするために使用されます。これは、ブラウザからのみデータをフェッチしたいページやプロジェクト全体に役立ちます。
// route("products/:pid", "./product.tsx");
import type { Route } from "./+types/product";
export async function clientLoader({
params,
}: Route.ClientLoaderArgs) {
const res = await fetch(`/api/products/${params.pid}`);
const product = await res.json();
return product;
}
// HydrateFallback は、クライアントローダーが実行中にレンダリングされます
export function HydrateFallback() {
return <div>Loading...</div>;
}
export default function Product({
loaderData,
}: Route.ComponentProps) {
const { name, description } = loaderData;
return (
<div>
<h1>{name}</h1>
<p>{description}</p>
</div>
);
}サーバーデータローディング
サーバーレンダリングの場合、loader は初期ページロードとクライアントナビゲーションの両方に使用されます。クライアントナビゲーションは、ブラウザからサーバーへの React Router による自動 fetch を通じてローダーを呼び出します。
// route("products/:pid", "./product.tsx");
import type { Route } from "./+types/product";
import { fakeDb } from "../db";
export async function loader({ params }: Route.LoaderArgs) {
const product = await fakeDb.getProduct(params.pid);
return product;
}
export default function Product({
loaderData,
}: Route.ComponentProps) {
const { name, description } = loaderData;
return (
<div>
<h1>{name}</h1>
<p>{description}</p>
</div>
);
}loader 関数はクライアントバンドルから削除されるため、ブラウザに含まれることを心配せずにサーバー専用の API を使用できます。
静的データローディング
プリレンダリングの場合、ローダーはプロダクションビルド中にデータをフェッチするために使用されます。
// route("products/:pid", "./product.tsx");
import type { Route } from "./+types/product";
export async function loader({ params }: Route.LoaderArgs) {
let product = await getProductFromCSVFile(params.pid);
return product;
}
export default function Product({
loaderData,
}: Route.ComponentProps) {
const { name, description } = loaderData;
return (
<div>
<h1>{name}</h1>
<p>{description}</p>
</div>
);
}プリレンダリングする URL は、react-router.config.ts で指定します。
import type { Config } from "@react-router/dev/config";
export default {
async prerender() {
let products = await readProductsFromCSVFile();
return products.map(
(product) => `/products/${product.id}`,
);
},
} satisfies Config;サーバーレンダリングの場合、プリレンダリングされていない URL は通常どおりサーバーレンダリングされるため、単一のルートで一部のデータをプリレンダリングしながら、残りをサーバーレンダリングできます。
両方のローダーの使用
loader と clientLoader は一緒に使用できます。loader は、初期 SSR (またはプリレンダリング) のためにサーバーで使用され、clientLoader は、後続のクライアント側のナビゲーションで使用されます。
// route("products/:pid", "./product.tsx");
import type { Route } from "./+types/product";
import { fakeDb } from "../db";
export async function loader({ params }: Route.LoaderArgs) {
return fakeDb.getProduct(params.pid);
}
export async function clientLoader({
serverLoader,
params,
}: Route.ClientLoaderArgs) {
const res = await fetch(`/api/products/${params.pid}`);
const serverData = await serverLoader();
return { ...serverData, ...res.json() };
}
export default function Product({
loaderData,
}: Route.ComponentProps) {
const { name, description } = loaderData;
return (
<div>
<h1>{name}</h1>
<p>{description}</p>
</div>
);
}関数の hydrate プロパティを設定して、ハイドレーション中およびページがレンダリングされる前にクライアントローダーを強制的に実行することもできます。この状況では、クライアントローダーの実行中にフォールバック UI を表示するために HydrateFallback コンポーネントをレンダリングする必要があります。
export async function loader() {
/* ... */
}
export async function clientLoader() {
/* ... */
}
// ハイドレーション中にクライアントローダーを強制的に実行します
clientLoader.hydrate = true as const; // 型推論のための `as const`
export function HydrateFallback() {
return <div>Loading...</div>;
}
export default function Product() {
/* ... */
}次: アクション
参照: