useFetcher
在 HTML/HTTP 中,数据变动和加载是通过导航来模拟的:<a href>
和 <form action>
。两者都会导致浏览器导航。React Router 的等效项是 <Link>
和 <Form>
。
但有时您想在导航之外调用 loader
,或者调用 action
(并在页面上获取数据以重新验证)而无需更改 URL。或者您需要同时进行多个变动。
许多与服务器的交互不是导航事件。此钩子允许您将 UI 连接到您的操作和加载器,而无需导航。
这在您需要以下情况时很有用
如果您正在构建高度交互式的“类似应用程序”的用户界面,您将经常使用 useFetcher
。
import { useFetcher } from "react-router-dom";
function SomeComponent() {
const fetcher = useFetcher();
// call submit or load in a useEffect
React.useEffect(() => {
fetcher.submit(data, options);
fetcher.load(href);
}, [fetcher]);
// build your UI with these properties
fetcher.state;
fetcher.formData;
fetcher.json;
fetcher.text;
fetcher.formMethod;
fetcher.formAction;
fetcher.data;
// render a form that doesn't cause navigation
return <fetcher.Form />;
}
Fetcher 具有许多内置行为
errorElement
来处理未捕获的错误(就像从 <Link>
或 <Form>
进行的正常导航一样)<Link>
或 <Form>
进行的正常导航一样)key
默认情况下,useFetcher
会生成一个作用域限定于该组件的唯一 fetcher(但是,它可以在 useFetchers()
中被查找,只要它处于活动状态)。如果您想使用自己的 key
来标识 fetcher,以便您可以从应用程序中的其他地方访问它,您可以使用 key
选项来实现
function AddToBagButton() {
const fetcher = useFetcher({ key: "add-to-bag" });
return <fetcher.Form method="post">...</fetcher.Form>;
}
// Then, up in the header...
function CartCount({ count }) {
const fetcher = useFetcher({ key: "add-to-bag" });
const inFlightCount = Number(
fetcher.formData?.get("quantity") || 0
);
const optimisticCount = count + inFlightCount;
return (
<>
<BagIcon />
<span>{optimisticCount}</span>
</>
);
}
fetcher.Form
与 <Form>
相同,只是它不会导致导航。 (希望您能克服 JSX 中的点!)
function SomeComponent() {
const fetcher = useFetcher();
return (
<fetcher.Form method="post" action="/some/route">
<input type="text" />
</fetcher.Form>
);
}
fetcher.load(href, options)
从路由加载器加载数据。
import { useFetcher } from "react-router-dom";
function SomeComponent() {
const fetcher = useFetcher();
useEffect(() => {
if (fetcher.state === "idle" && !fetcher.data) {
fetcher.load("/some/route");
}
}, [fetcher]);
return <div>{fetcher.data || "Loading..."}</div>;
}
虽然一个 URL 可能匹配多个嵌套路由,但 fetcher.load()
调用只会调用叶子匹配的加载器(或 索引路由 的父级)。
如果您发现自己在点击处理程序中调用此函数,您可能可以通过使用 <fetcher.Form>
来简化您的代码。
fetcher.load
调用都将在重新验证时重新执行(在导航提交、另一个 fetcher 提交或 useRevalidator()
调用之后)。
options.unstable_flushSync
unstable_flushSync
选项告诉 React Router DOM 将此 fetcher.load
的初始状态更新包装在一个 ReactDOM.flushSync
调用中,而不是默认的 React.startTransition
。这允许您在更新刷新到 DOM 后立即执行同步 DOM 操作。
fetcher.submit()
<fetcher.Form>
的命令式版本。如果用户交互应该启动获取,您应该使用 <fetcher.Form>
。但是,如果您(程序员)正在启动获取(不是响应用户点击按钮等),那么请使用此函数。
例如,您可能希望在一定时间的空闲时间后注销用户
import { useFetcher } from "react-router-dom";
import { useFakeUserIsIdle } from "./fake/hooks";
export function useIdleLogout() {
const fetcher = useFetcher();
const userIsIdle = useFakeUserIsIdle();
useEffect(() => {
if (userIsIdle) {
fetcher.submit(
{ idle: true },
{ method: "post", action: "/logout" }
);
}
}, [userIsIdle]);
}
fetcher.submit
是对 fetcher 实例的 useSubmit
调用的包装器,因此它也接受与 useSubmit
相同的选项。
如果要提交到索引路由,请使用 ?index
参数。
如果您发现自己在点击处理程序中调用此函数,您可能可以通过使用 <fetcher.Form>
来简化您的代码。
fetcher.state
您可以使用 fetcher.state
来了解 fetcher 的状态。它将是以下之一:
fetcher.load
)或在单独的提交或 useRevalidator
调用后正在重新验证。fetcher.data
加载器或操作返回的数据存储在此处。一旦数据设置,它将保留在 fetcher 上,即使在重新加载和重新提交时也是如此。
function ProductDetails({ product }) {
const fetcher = useFetcher();
return (
<details
onToggle={(event) => {
if (
event.currentTarget.open &&
fetcher.state === "idle" &&
!fetcher.data
) {
fetcher.load(`/product/${product.id}/details`);
}
}}
>
<summary>{product.name}</summary>
{fetcher.data ? (
<div>{fetcher.data}</div>
) : (
<div>Loading product details...</div>
)}
</details>
);
}
fetcher.formData
当使用 <fetcher.Form>
或 fetcher.submit()
时,表单数据可用于构建乐观 UI。
function TaskCheckbox({ task }) {
let fetcher = useFetcher();
// while data is in flight, use that to immediately render
// the state you expect the task to be in when the form
// submission completes, instead of waiting for the
// network to respond. When the network responds, the
// formData will no longer be available and the UI will
// use the value in `task.status` from the revalidation
let status =
fetcher.formData?.get("status") || task.status;
let isComplete = status === "complete";
return (
<fetcher.Form method="post">
<button
type="submit"
name="status"
value={isComplete ? "complete" : "incomplete"}
>
{isComplete ? "Mark Complete" : "Mark Incomplete"}
</button>
</fetcher.Form>
);
}
fetcher.json
当使用 fetcher.submit(data, { formEncType: "application/json" })
时,提交的 JSON 可通过 fetcher.json
获取。
fetcher.text
当使用 fetcher.submit(data, { formEncType: "text/plain" })
时,提交的文本可通过 fetcher.text
获取。
fetcher.formAction
告诉您表单提交到的操作 URL。
<fetcher.Form action="/mark-as-read" />;
// when the form is submitting
fetcher.formAction; // "mark-as-read"
fetcher.formMethod
告诉您正在提交的表单的方法:get、post、put、patch 或 delete。
<fetcher.Form method="post" />;
// when the form is submitting
fetcher.formMethod; // "post"
fetcher.formMethod
字段为小写,没有 future.v7_normalizeFormMethod
未来标志。这正在被规范化为大写以与 v7 中的 fetch()
行为保持一致,因此请升级您的 React Router v6 应用程序以采用大写 HTTP 方法。