資料從 loader
和 clientLoader
提供給路由元件。
Loader 資料會自動從 loaders 序列化,並在元件中反序列化。除了字串和數字等基本值之外,loaders 也可以傳回 promises、maps、sets、dates 等。
`loaderData` prop 的類型會自動產生。
`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 is rendered while the client loader is running
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
呼叫 loader。
// 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
函數會從客戶端 bundle 中移除,因此您可以使用僅限伺服器的 API,而無需擔心它們包含在瀏覽器中。
當預先渲染時,loaders 用於在生產建置期間提取資料。
// 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` 屬性,強制客戶端 loader 在 hydration 期間和頁面渲染之前執行。在這種情況下,您會想要渲染一個 `HydrateFallback` 元件,以在客戶端 loader 執行時顯示備用 UI。
export async function loader() {
/* ... */
}
export async function clientLoader() {
/* ... */
}
// force the client loader to run during hydration
clientLoader.hydrate = true as const; // `as const` for type inference
export function HydrateFallback() {
return <div>Loading...</div>;
}
export default function Product() {
/* ... */
}
下一步:動作
另請參閱