Remix 全栈数据流

全栈数据流

Remix 的主要功能之一是它如何自动使您的 UI 与持久服务器状态保持同步。它分三个步骤进行

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

路由模块导出

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

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

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

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

路由加载器

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

javascript
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读取加载器数据

javascript
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) getter 耦合。

javascript
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 将表单数据发送到路由操作,并且可以通过 useNavigation 和 useFetcher 等 hook 获得待处理状态。
  2. 操作完成后,将重新验证加载器以获取新的服务器状态。
  3. useLoaderData 返回服务器的更新值,并且待处理状态恢复为空闲。

通过这种方式,UI 可以与服务器状态保持同步,而无需编写任何代码来进行同步。

除了 HTML 表单元素(例如,响应拖放或 onChange 事件)之外,还有多种提交表单的方法。关于表单验证、错误处理、待处理状态等,还有很多要讨论的内容。我们稍后会讨论所有这些,但这只是 Remix 中数据流的要点。

在 JavaScript 加载之前

当您从服务器发送 HTML 时,最好让它在 JavaScript 加载之前也能工作。Remix 中的典型数据流会自动执行此操作。流程相同,但浏览器会完成一些工作。

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

  1. 浏览器将表单提交到操作(而不是 fetch),并且浏览器的待处理状态会激活(旋转的网站图标)
  2. 操作完成后,将调用加载器
  3. Remix 渲染页面并将 HTML 发送到浏览器