主分支
分支
主分支(6.23.1)開發分支
版本
6.23.1v4/5.xv3.x
useFetchers

useFetchers

傳回一個陣列,其中包含所有飛行中的 fetchers,不包含它們的 loadsubmitForm 屬性(無法讓父層元件試圖控制子層元件的行為!我們從現實生活中知道這根本就是徒勞無功。)

此功能僅在使用數據路由器時有效,請參閱 選擇路由器

import { useFetchers } from "react-router-dom";

function SomeComp() {
  const fetchers = useFetchers();
  // array of inflight fetchers
}

這對應用程式中沒有建立 fetchers 但想使用其提交內容參與樂觀 UI 的元件很有用。

例如,想像一個使用者介面,其中側邊欄列出專案,而主畫面顯示目前專案核取方塊的清單。側邊欄可以顯示每個專案完成的任務數和總任務數。

+-----------------+----------------------------+
|                 |                            |
|   Soccer  (8/9) | [x] Do the dishes          |
|                 |                            |
| > Home    (2/4) | [x] Fold laundry           |
|                 |                            |
|                 | [ ] Replace battery in the |
|                 |     smoke alarm            |
|                 |                            |
|                 | [ ] Change lights in kids  |
|                 |     bathroom               |
|                 |                            |
+-----------------+----------------------------┘

當使用者點選核取方塊時,提交內容會傳送至動作以變更任務的狀態。我們不想要建立「載入狀態」,而是想要建立一個「樂觀 UI」,即使伺服器尚未處理核取方塊,也會立即更新核取方塊以顯示已勾選。在核取方塊元件中,我們可以使用 fetcher.formData

function Task({ task }) {
  const { projectId, id } = task;
  const toggle = useFetcher();
  const checked = toggle.formData
    ? toggle.formData.get("complete") === "on"
    : task.complete;

  return (
    <toggle.Form
      method="put"
      action={`/projects/${projectId}/tasks/${id}`}
    >
      <input name="id" type="hidden" defaultValue={id} />
      <label>
        <input
          name="complete"
          type="checkbox"
          checked={checked}
          onChange={(e) => toggle.submit(e.target.form)}
        />
      </label>
    </toggle.Form>
  );
}

這對核取方塊而言很棒,但當使用者點選其中一個核取方塊時,側邊欄會顯示 2/4 而核取方塊會顯示 3/4!

+-----------------+----------------------------+
|                 |                            |
|   Soccer  (8/9) | [x] Do the dishes          |
|                 |                            |
| > Home    (2/4) | [x] Fold laundry           |
|     WRONG! ^    |                            |
|          CLICK!-->[x] Replace battery in the |
|                 |     smoke alarm            |
|                 |                            |
|                 | [ ] Change lights in kids  |
|                 |     bathroom               |
|                 |                            |
+-----------------+----------------------------┘

由於路由會自動重新驗證,因此側邊欄會迅速更新並正確無誤。但是,在短暫的時間內,它會有點怪怪的。

這是 useFetchers 出場的地方。在側邊欄中,我們可以透過檢查方塊來存取所有傳送中的 fetcher 狀態 - 即使它不是建立這些狀態的元件。

策略有三個步驟

  1. 在特定專案中尋找任務的提交
  2. 使用 fetcher.formData 立即更新次數
  3. 如果任務不是傳送中,則使用一般任務的狀態
function ProjectTaskCount({ project }) {
  let completedTasks = 0;
  const fetchers = useFetchers();

  // Find this project's fetchers
  const relevantFetchers = fetchers.filter((fetcher) => {
    return fetcher.formAction?.startsWith(
      `/projects/${project.id}/tasks/`
    );
  });

  // Store in a map for easy lookup
  const myFetchers = new Map(
    relevantFetchers.map(({ formData }) => [
      formData.get("id"),
      formData.get("complete") === "on",
    ])
  );

  // Increment the count
  for (const task of project.tasks) {
    if (myFetchers.has(task.id)) {
      if (myFetchers.get(task.id)) {
        // if it's being submitted, increment optimistically
        completedTasks++;
      }
    } else if (task.complete) {
      // otherwise use the real task's data
      completedTasks++;
    }
  }

  return (
    <small>
      {completedTasks}/{project.tasks.length}
    </small>
  );
}

這需要花一些功夫,但大部分只是要求 React Router 提供它追蹤中的狀態,並根據狀態進行樂觀計算。