React Router v7 已發布。 查看文件
loader
本頁內容

loader

觀看 📼 Remix Single: 將資料載入到元件中

每個路由都可以定義一個 loader 函式,該函式在渲染時為路由提供資料。

import { json } from "@remix-run/node"; // or cloudflare/deno

export const loader = async () => {
  return json({ ok: true });
};

此函式僅在伺服器上執行。在初始伺服器渲染時,它將向 HTML 文件提供資料。在瀏覽器中的導航時,Remix 將通過 fetch 從瀏覽器呼叫該函式。

這表示您可以直接與您的資料庫交談,使用僅限伺服器的 API 密碼等。任何未用於渲染 UI 的程式碼都將從瀏覽器套件中移除。

以使用資料庫 ORM Prisma 作為範例

import { useLoaderData } from "@remix-run/react";

import { prisma } from "../db";

export async function loader() {
  return json(await prisma.user.findMany());
}

export default function Users() {
  const data = useLoaderData<typeof loader>();
  return (
    <ul>
      {data.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

由於 prisma 僅在 loader 中使用,因此編譯器會將其從瀏覽器套件中移除,如反白顯示的行所示。

請注意,無論您從 `loader` 傳回什麼,都會暴露給客戶端,即使元件沒有渲染它。請像對待公共 API 端點一樣小心對待您的 `loader`。

型別安全

您可以通過 useLoaderData<typeof loader> 取得網路中 loader 和元件的型別安全。

import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";

export async function loader() {
  return json({ name: "Ryan", date: new Date() });
}

export default function SomeRoute() {
  const data = useLoaderData<typeof loader>();
}
  • data.name 將知道它是一個字串
  • data.date 也會知道它是一個字串,即使我們將日期物件傳遞給 json。當為了客戶端轉換而提取資料時,這些值會使用 JSON.stringify 在網路上序列化,並且這些型別知道這一點

params

路由參數由路由檔案名稱定義。如果某個段以 $ 開頭,如 $invoiceId,則該段的 URL 值將傳遞給您的 loader

// if the user visits /invoices/123
export async function loader({
  params,
}: LoaderFunctionArgs) {
  params.invoiceId; // "123"
}

參數主要用於通過 ID 查找記錄

// if the user visits /invoices/123
export async function loader({
  params,
}: LoaderFunctionArgs) {
  const invoice = await fakeDb.getInvoice(params.invoiceId);
  if (!invoice) throw new Response("", { status: 404 });
  return json(invoice);
}

request

這是 Fetch Request 實例。您可以閱讀 MDN 文件以查看其所有屬性。

loader 中最常見的用例是讀取 headers (如 cookies) 和請求中的 URL URLSearchParams

export async function loader({
  request,
}: LoaderFunctionArgs) {
  // read a cookie
  const cookie = request.headers.get("Cookie");

  // parse the search params for `?q=`
  const url = new URL(request.url);
  const query = url.searchParams.get("q");
}

context

這是傳遞到您的伺服器適配器 getLoadContext() 函式中的 context。它是彌合適配器的請求/回應 API 與您的 Remix 應用程式之間差距的一種方式。

這個 API 是一種緊急出口,通常不太需要用到。

以 express 適配器為例

const {
  createRequestHandler,
} = require("@remix-run/express");

app.all(
  "*",
  createRequestHandler({
    getLoadContext(req, res) {
      // this becomes the loader context
      return { expressUser: req.user };
    },
  })
);

然後您的 loader 就可以存取它。

export async function loader({
  context,
}: LoaderFunctionArgs) {
  const { expressUser } = context;
  // ...
}

回傳 Response 實例

您需要從您的 loader 中回傳一個 Fetch Response

export async function loader() {
  const users = await db.users.findMany();
  const body = JSON.stringify(users);
  return new Response(body, {
    headers: {
      "Content-Type": "application/json",
    },
  });
}

使用 json 輔助函數可以簡化這個過程,讓您不必自己建構它們,但這兩個範例實際上是相同的!

import { json } from "@remix-run/node"; // or cloudflare/deno

export const loader = async () => {
  const users = await fakeDb.users.findMany();
  return json(users);
};

您可以看到 json 如何只是做了一小部分工作,就讓您的 loader 清晰很多。您也可以使用 json 輔助函數來為您的回應添加標頭或狀態碼。

import { json } from "@remix-run/node"; // or cloudflare/deno

export const loader = async ({
  params,
}: LoaderFunctionArgs) => {
  const project = await fakeDb.project.findOne({
    where: { id: params.id },
  });

  if (!project) {
    return json("Project not found", { status: 404 });
  }

  return json(project);
};

另請參閱

在 Loaders 中拋出 Responses

除了回傳回應之外,您也可以從您的 loader 中拋出 Response 物件。這允許您中斷呼叫堆疊並執行以下兩件事之一:

  • 重新導向到另一個 URL
  • 透過 ErrorBoundary 顯示具有上下文資料的替代 UI

這是一個完整的範例,展示了如何建立拋出 responses 的工具函式,以停止 loader 中的程式碼執行並顯示替代 UI。

import { json } from "@remix-run/node"; // or cloudflare/deno

export function getInvoice(id) {
  const invoice = db.invoice.find({ where: { id } });
  if (invoice === null) {
    throw json("Not Found", { status: 404 });
  }
  return invoice;
}
import { redirect } from "@remix-run/node"; // or cloudflare/deno

import { getSession } from "./session";

export async function requireUserSession(request) {
  const session = await getSession(
    request.headers.get("cookie")
  );
  if (!session) {
    // You can throw our helpers like `redirect` and `json` because they
    // return `Response` objects. A `redirect` response will redirect to
    // another URL, while other  responses will trigger the UI rendered
    // in the `ErrorBoundary`.
    throw redirect("/login", 302);
  }
  return session.get("user");
}
import type { LoaderFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno
import {
  isRouteErrorResponse,
  useLoaderData,
  useRouteError,
} from "@remix-run/react";

import { getInvoice } from "~/db";
import { requireUserSession } from "~/http";

export const loader = async ({
  params,
  request,
}: LoaderFunctionArgs) => {
  const user = await requireUserSession(request);
  const invoice = getInvoice(params.invoiceId);

  if (!invoice.userIds.includes(user.id)) {
    throw json(
      { invoiceOwnerEmail: invoice.owner.email },
      { status: 401 }
    );
  }

  return json(invoice);
};

export default function InvoiceRoute() {
  const invoice = useLoaderData<typeof loader>();
  return <InvoiceView invoice={invoice} />;
}

export function ErrorBoundary() {
  const error = useRouteError();

  if (isRouteErrorResponse(error)) {
    switch (error.status) {
      case 401:
        return (
          <div>
            <p>You don't have access to this invoice.</p>
            <p>
              Contact {error.data.invoiceOwnerEmail} to get
              access
            </p>
          </div>
        );
      case 404:
        return <div>Invoice not found!</div>;
    }

    return (
      <div>
        Something went wrong: {error.status}{" "}
        {error.statusText}
      </div>
    );
  }

  return (
    <div>
      Something went wrong:{" "}
      {error?.message || "Unknown Error"}
    </div>
  );
}
文件與範例以 MIT