資料載入
本頁內容

資料載入

簡介

資料透過 loaderclientLoader 提供給路由元件。

載入器資料會自動從載入器中序列化,並在元件中反序列化。除了字串和數字等原始值外,載入器還可以返回 Promise、Map、Set、Date 等。

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 呼叫載入器。

// 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 都會像往常一樣進行服務端渲染,這樣你就可以在單個路由上預渲染一些資料,同時仍然對其他資料進行服務端渲染。

同時使用兩種載入器

loaderclientLoader 可以一起使用。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 屬性,強制客戶端載入器在 hydration 期間和頁面渲染之前執行。在這種情況下,你需要渲染一個 HydrateFallback 元件,以在客戶端載入器執行時顯示一個後備 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() {
  /* ... */
}

下一步:操作

另請參閱

文件和示例 CC 4.0
編輯