Remix 全栈数据流
全栈数据流
Remix 的主要功能之一是它如何自动使您的 UI 与持久服务器状态保持同步。它分三个步骤进行
- 路由加载器向 UI 提供数据
- 表单将数据发布到更新持久状态的路由操作
- 页面上的加载器数据会自动重新验证
路由模块导出
让我们考虑一个用户帐户编辑路由。路由模块有三个导出,我们将填充并讨论它们
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 }); }
提交和重新验证
当用户提交表单时
- Remix 通过 fetch 将表单数据发送到路由操作,并且可以通过 useNavigation 和 useFetcher 等 hook 获得待处理状态。
- 操作完成后,将重新验证加载器以获取新的服务器状态。
- useLoaderData 返回服务器的更新值,并且待处理状态恢复为空闲。
通过这种方式,UI 可以与服务器状态保持同步,而无需编写任何代码来进行同步。
除了 HTML 表单元素(例如,响应拖放或 onChange 事件)之外,还有多种提交表单的方法。关于表单验证、错误处理、待处理状态等,还有很多要讨论的内容。我们稍后会讨论所有这些,但这只是 Remix 中数据流的要点。
在 JavaScript 加载之前
当您从服务器发送 HTML 时,最好让它在 JavaScript 加载之前也能工作。Remix 中的典型数据流会自动执行此操作。流程相同,但浏览器会完成一些工作。
当用户在 JavaScript 加载之前提交表单时
- 浏览器将表单提交到操作(而不是 fetch),并且浏览器的待处理状态会激活(旋转的网站图标)
- 操作完成后,将调用加载器
- Remix 渲染页面并将 HTML 发送到浏览器