全栈数据流
在本页

全栈数据流

Remix 的主要功能之一是它自动将您的 UI 与持久服务器状态保持同步的方式。 它分为三个步骤。

  1. 路由加载器向 UI 提供数据
  2. 表单将数据发布到更新持久状态的路由操作
  3. 页面上的加载器数据会自动重新验证

路由模块导出

让我们考虑一个用户帐户编辑路由。 路由模块有三个导出,我们将填写它们并进行讨论

export async function loader() {
  // provides data to the component
}

export default function Component() {
  // renders the UI
}

export async function action() {
  // updates persistent data
}

路由加载器

路由文件可以导出一个 loader 函数,该函数为路由组件提供数据。 当用户导航到匹配的路由时,首先加载数据,然后渲染页面。

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

export async function loader({
  request,
}: LoaderFunctionArgs) {
  const user = await getUser(request);
  return json({
    displayName: user.displayName,
    email: user.email,
  });
}

export default function Component() {
  // ...
}

export async function action() {
  // ...
}

路由组件

路由文件的默认导出是渲染的组件。 它使用 useLoaderData 读取加载器数据

import type { LoaderFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno
import { useLoaderData, Form } from "@remix-run/react";

export async function loader({
  request,
}: LoaderFunctionArgs) {
  const user = await getUser(request);
  return json({
    displayName: user.displayName,
    email: user.email,
  });
}

export default function Component() {
  const user = useLoaderData<typeof loader>();
  return (
    <Form method="post" action="/account">
      <h1>Settings for {user.displayName}</h1>

      <input
        name="displayName"
        defaultValue={user.displayName}
      />
      <input name="email" defaultValue={user.email} />

      <button type="submit">Save</button>
    </Form>
  );
}

export async function action() {
  // ...
}

路由操作

最后,当表单提交时,与表单的 action 属性匹配的路由上的操作会被调用。 在此示例中,它是同一路由。 表单字段中的值将可以在标准 request.formData() API 上获得。 请注意,输入上的 name 属性与 formData.get(fieldName) 获取器相耦合。

import type {
  ActionFunctionArgs,
  LoaderFunctionArgs,
} from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno
import { useLoaderData, Form } from "@remix-run/react";

export async function loader({
  request,
}: LoaderFunctionArgs) {
  const user = await getUser(request);
  return json({
    displayName: user.displayName,
    email: user.email,
  });
}

export default function Component() {
  const user = useLoaderData<typeof loader>();
  return (
    <Form method="post" action="/account">
      <h1>Settings for {user.displayName}</h1>

      <input
        name="displayName"
        defaultValue={user.displayName}
      />
      <input name="email" defaultValue={user.email} />

      <button type="submit">Save</button>
    </Form>
  );
}

export async function action({
  request,
}: ActionFunctionArgs) {
  const formData = await request.formData();
  const user = await getUser(request);

  await updateUser(user.id, {
    email: formData.get("email"),
    displayName: formData.get("displayName"),
  });

  return json({ ok: true });
}

提交和重新验证

当用户提交表单时

  1. Remix 通过 fetch 将表单数据发送到路由操作,并且挂起状态通过诸如 useNavigationuseFetcher 之类的挂钩变得可用。
  2. 操作完成后,加载器会重新验证以获取新的服务器状态。
  3. useLoaderData 返回来自服务器的更新值,并且待处理状态恢复到空闲状态。

这样,UI 就可以与服务器状态保持同步,无需编写任何同步代码。

除了 HTML 表单元素之外,还有多种方法可以提交表单(例如,响应拖放或 onChange 事件)。关于表单验证、错误处理、待处理状态等方面还有很多需要讨论的内容。我们将在后面详细讲解,但这就是 Remix 中数据流的概要。

JavaScript 加载之前

从服务器发送 HTML 时,最好使其在 JavaScript 加载之前也能正常工作。Remix 中典型的数据流会自动执行此操作。流程相同,但浏览器会完成部分工作。

当用户在 JavaScript 加载之前提交表单时

  1. 浏览器将表单提交到操作(而不是 fetch),并且浏览器待处理状态激活(旋转的 favicon)。
  2. 操作完成后,会调用加载器。
  3. Remix 渲染页面并将 HTML 发送到浏览器。
文档和示例使用 MIT