主分支
分支
主分支 (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 的组件很有用。

例如,想象一个 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 请求它正在跟踪的状态,并根据它进行乐观计算。