Множественные fetcher на одной странице

React Router начиная с версии 6.4 ввел концепцию fetcher, которая позволяет выполнять загрузку данных, не используя обычный компонент <Route> и не вызывая перерендер всей страницы. Fetcher предоставляет удобный способ работы с асинхронными запросами и действиями (actions) прямо внутри компонентов. Рассмотрим, как работать с несколькими fetcher на одной странице.


Создание fetcher

Для использования fetcher необходимо импортировать хук useFetcher из react-router-dom:

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

function MyComponent() {
  const fetcher = useFetcher();
  return (
    <fetcher.Form method="post" action="/submit">
      <input type="text" name="name" />
      <button type="submit">Отправить</button>
    </fetcher.Form>
  );
}

Ключевые моменты:

  • useFetcher() возвращает объект fetcher с состояниями state, data, formData.
  • <fetcher.Form> работает аналогично <form> с поддержкой реактивного состояния.

Несколько fetcher на одной странице

На одной странице можно создать несколько независимых fetcher. Это позволяет каждому fetcher управлять своей областью данных, не влияя на другие.

function MultiFetcherPage() {
  const fetcherUser = useFetcher();
  const fetcherPosts = useFetcher();

  return (
    <div>
      <fetcherUser.Form method="post" action="/update-user">
        <input type="text" name="username" placeholder="Имя пользователя" />
        <button type="submit">
          {fetcherUser.state === "submitting" ? "Сохраняем..." : "Сохранить"}
        </button>
      </fetcherUser.Form>

      <fetcherPosts.Form method="post" action="/add-post">
        <input type="text" name="title" placeholder="Название поста" />
        <button type="submit">
          {fetcherPosts.state === "submitting" ? "Добавляем..." : "Добавить"}
        </button>
      </fetcherPosts.Form>

      <div>
        <h3>Состояние fetcherUser:</h3>
        <pre>{JSON.stringify(fetcherUser.data, null, 2)}</pre>
      </div>

      <div>
        <h3>Состояние fetcherPosts:</h3>
        <pre>{JSON.stringify(fetcherPosts.data, null, 2)}</pre>
      </div>
    </div>
  );
}

Особенности:

  • Каждое состояние fetcher (state, data) отслеживается отдельно.
  • Fetcher не блокирует другие fetcher. Можно отправлять несколько форм одновременно.
  • fetcher.Form поддерживает методы get и post, что позволяет гибко конфигурировать асинхронные действия.

Обработка состояний fetcher

Каждый fetcher имеет следующие ключевые свойства:

  • state: "idle" | "submitting" | "loading". Используется для отображения индикаторов загрузки.
  • data: результат действия или загрузки данных.
  • formData: текущие данные формы (если используется <fetcher.Form>).
const fetcher = useFetcher();

useEffect(() => {
  if (fetcher.state === "submitting") {
    console.log("Форма отправляется");
  }
  if (fetcher.state === "loading") {
    console.log("Загрузка данных");
  }
}, [fetcher.state]);

Примеры взаимодействия нескольких fetcher

  1. Синхронное обновление нескольких областей:
const fetcherA = useFetcher();
const fetcherB = useFetcher();

const handleUpdate = () => {
  fetcherA.submit({ id: 1 }, { method: "post", action: "/updateA" });
  fetcherB.submit({ id: 2 }, { method: "post", action: "/updateB" });
};
  1. Параллельные асинхронные действия без конфликтов:
<fetcherA.Form method="post" action="/saveA">
  <button>Сохранить A</button>
</fetcherA.Form>

<fetcherB.Form method="post" action="/saveB">
  <button>Сохранить B</button>
</fetcherB.Form>

Каждая форма будет работать независимо, не блокируя другую.


Взаимодействие fetcher с loader и action

Fetchers могут работать совместно с loader и action маршрута. Если необходимо, fetcher может инициировать загрузку данных маршрута:

fetcher.load("/route-loader-path");

Это позволяет обновлять состояние страницы или части интерфейса без полной перезагрузки маршрута.


Лучшие практики

  • Использовать отдельный fetcher для каждой формы или логической области данных.
  • Не смешивать fetcher с состояниями локального useState без необходимости.
  • Использовать state fetcher для отображения индикаторов загрузки и блокировки кнопок.
  • Для сложных страниц с большим количеством асинхронных операций fetcher обеспечивает масштабируемость и чистоту кода.

Множественные fetcher позволяют строить комплексные интерфейсы с независимыми асинхронными действиями, сохраняя реактивность и минимизируя повторные рендеры компонентов. Они идеально подходят для панелей управления, дашбордов и страниц с большим количеством форм.