useFetcher
在 HTML/HTTP 中,資料變動和載入會以導覽的方式進行建模:<a href>
和 <form action>
。這兩個都會在瀏覽器中造成導覽。React Router 等效的程式碼分別為 <Link>
和 <Form>
。
不過,有時候你會想要在導覽以外的地點呼叫 loader
,或是呼叫 action
(並讓頁面上的資料重新驗證)而不改變 URL。或者你可能會需要在同一時間處理多個進行中的變動。
對伺服器的許多互動都不是導航事件。這個 Hook 能讓你在不導航的情況下,將你的 UI 插入你的 action 和 loader 中。
在你需要以下功能時,這會很有用:
如果您正在建構一個互動度高的「類似 app」的使用者介面,您將會經常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 />;
}
Fetchers 具有多種內建行為
errorElement
處理未捕捉到的錯誤(就像從 <Link>
或 <Form>
進行一般導覽一樣)<Link>
或 <Form>
進行一般導覽一樣)key
預設情況下,useFetcher
會產生一個範圍限定在該元件的獨特取用器(不過,可以在中繼進行時,在useFetchers()
中查詢)。如果您想使用自己的key
識別一個取用器,以便您可以從 app 中其他位置存取它,您可以使用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
呼叫都將作為重新驗證的一部分重新執行(在導覽提交、其他取用器提交或 useRevalidator()
呼叫之後)
options.unstable_flushSync
unstable_flushSync
選項會告訴 React Router DOM 使用 ReactDOM.flushSync
呼叫來包裝此 fetcher.load
的初始狀態更新,而不是預設的 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()
時,表單資料會可用於建立樂觀使用者介面。
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
告訴您表單提交的動作網址。
<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 方法。