主程式碼
分支
主程式碼 (6.23.1)dev
版本
6.23.1v4/5.xv3.x
useFetcher
在此頁面上

useFetcher

在 HTML/HTTP 中,資料變動和載入會以導覽的方式進行建模:<a href><form action>。這兩個都會在瀏覽器中造成導覽。React Router 等效的程式碼分別為 <Link><Form>

不過,有時候你會想要在導覽以外的地點呼叫 loader,或是呼叫 action(並讓頁面上的資料重新驗證)而不改變 URL。或者你可能會需要在同一時間處理多個進行中的變動。

對伺服器的許多互動都不是導航事件。這個 Hook 能讓你在不導航的情況下,將你的 UI 插入你的 action 和 loader 中。

此功能僅在使用資料路由時才有效,請參閱 選擇路由器

在你需要以下功能時,這會很有用:

  • 擷取與 UI 路由無關的資料(提示視窗、動態表單等)
  • 向 action 提交資料而不導航(電子報註冊等共用元件)
  • 在清單中處理多個同時提交的項目(典型的「待辦事項 App」清單,你可以按下多個按鈕,且所有按鈕都可以在同一時間保持待處理狀態)
  • 無限捲動容器
  • 還有更多!

如果您正在建構一個互動度高的「類似 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 具有多種內建行為

  • 在中斷取用時,會自動處理取消
  • 使用 POST、PUT、PATCH、DELETE 提交時,會先呼叫動作
    • 動作完成後,頁面上的資料會重新驗證,以擷取可能已發生的任何突變,自動讓您的 UI 與伺服器狀態同步
  • 當多個取用同時發生時,它將
    • 提交最新可用的資料,因為它們各自達成
    • 確保沒有舊載入覆寫較新的資料,不管回應回傳的順序為何
  • 透過呈現最近的errorElement處理未捕捉到的錯誤(就像從 <Link><Form> 進行一般導覽一樣)
  • 如果您的動作/載入器呼叫返回重新導向,將會重新導向 app (就像從 <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 動作。

請注意,這個 API 標記為不穩定,可能會受到重大的版本發布的影響而中斷變更

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 的狀態。它會是其中之一

  • 空閒 - 沒有任何正在擷取的內容。
  • 提交中 - 由於使用 POST、PUT、PATCH 或 DELETE 對 fetcher 提交動作的路由動作正在呼叫中
  • 載入中 - 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 方法。