React Router начиная с версии 6.4 ввел концепцию
fetcher, которая позволяет выполнять загрузку данных,
не используя обычный компонент <Route> и не вызывая
перерендер всей страницы. Fetcher предоставляет удобный способ работы с
асинхронными запросами и действиями (actions) прямо внутри компонентов.
Рассмотрим, как работать с несколькими 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 управлять своей областью данных, не влияя на другие.
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>
);
}
Особенности:
state, data)
отслеживается отдельно.fetcher.Form поддерживает методы get и
post, что позволяет гибко конфигурировать асинхронные
действия.Каждый 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]);
const fetcherA = useFetcher();
const fetcherB = useFetcher();
const handleUpdate = () => {
fetcherA.submit({ id: 1 }, { method: "post", action: "/updateA" });
fetcherB.submit({ id: 2 }, { method: "post", action: "/updateB" });
};
<fetcherA.Form method="post" action="/saveA">
<button>Сохранить A</button>
</fetcherA.Form>
<fetcherB.Form method="post" action="/saveB">
<button>Сохранить B</button>
</fetcherB.Form>
Каждая форма будет работать независимо, не блокируя другую.
Fetchers могут работать совместно с loader и action маршрута. Если необходимо, fetcher может инициировать загрузку данных маршрута:
fetcher.load("/route-loader-path");
Это позволяет обновлять состояние страницы или части интерфейса без полной перезагрузки маршрута.
state fetcher для отображения индикаторов
загрузки и блокировки кнопок.Множественные fetcher позволяют строить комплексные интерфейсы с независимыми асинхронными действиями, сохраняя реактивность и минимизируя повторные рендеры компонентов. Они идеально подходят для панелей управления, дашбордов и страниц с большим количеством форм.