数据通过 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
函数已从客户端 bundles 中移除,因此您可以使用仅限服务端的 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() {
/* ... */
}
下一步:操作
另请参阅