Хук useLoaderData является одним из ключевых
инструментов для работы с асинхронными данными в приложениях на React с
использованием React Router. Он позволяет компонентам получать данные,
предварительно загруженные в маршрутах, без необходимости вручную
управлять состоянием загрузки или эффектами.
useLoaderData используется в сочетании с функцией
loader, которая определяется на уровне маршрута. Функция
loader выполняется до рендера компонента, привязанного к
этому маршруту, что позволяет:
useEffect для
первичной загрузки данных.import { useLoaderData } from "react-router-dom";
function Profile() {
const data = useLoaderData();
return (
<div>
<h1>{data.name}</h1>
<p>{data.email}</p>
</div>
);
}
В этом примере data — это объект, возвращённый функцией
loader для маршрута, на котором рендерится компонент
Profile.
Функция loader определяется при настройке маршрута:
import { createBrowserRouter } from "react-router-dom";
import Profile from "./Profile";
const router = createBrowserRouter([
{
path: "/profile/:userId",
element: <Profile />,
loader: async ({ params }) => {
const response = await fetch(`/api/users/${params.userId}`);
if (!response.ok) {
throw new Response("User not found", { status: 404 });
}
return response.json();
},
},
]);
Особенности функции loader:
params, request
и context.Response для обработки ошибок на
уровне маршрута.useLoaderData автоматически связывает данные с текущим
маршрутом, включая динамические параметры:
const { userId } = useParams(); // использование для других целей
const user = useLoaderData(); // данные, загруженные loader
loader получает параметры маршрута через
params, что упрощает работу с динамическими URL:
loader: async ({ params }) => {
return fetch(`/api/posts/${params.postId}`).then(res => res.json());
}
Функция loader может бросать исключения или возвращать
специальные объекты для редиректа:
import { redirect } from "react-router-dom";
loader: async ({ params }) => {
const response = await fetch(`/api/items/${params.id}`);
if (response.status === 404) {
return redirect("/not-found");
}
return response.json();
}
Компонент, использующий useLoaderData, получает либо
данные, либо маршрут не будет отображен, если выполнен редирект.
useLoaderData работает синхронно в компоненте, но данные
фактически загружаются асинхронно до рендера маршрута. Это позволяет
интегрировать React Router с Suspense для отображения
индикатора загрузки:
import { Await, useLoaderData } from "react-router-dom";
import { Suspense } from "react";
function Dashboard() {
const dataPromise = useLoaderData();
return (
<Suspense fallback={<div>Загрузка данных...</div>}>
<Await resolve={dataPromise}>
{(data) => (
<div>
<h2>{data.title}</h2>
<p>{data.description}</p>
</div>
)}
</Await>
</Suspense>
);
}
Это позволяет обрабатывать промисы непосредственно в компоненте без
создания отдельного состояния загрузки через useState и
useEffect.
useLoaderData также может получать данные, переданные
через контекст маршрута, что полезно для передачи общих ресурсов:
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
loader: async () => ({ theme: "dark" }),
children: [
{
path: "settings",
element: <Settings />,
},
],
},
]);
function Settings() {
const { theme } = useLoaderData(); // наследует данные родительского loader
return <div>Текущая тема: {theme}</div>;
}
useLoaderDatauseLoaderData только для данных,
необходимых для первоначального рендера маршрута;useEffect или state management;loader с помощью
throw new Response или собственного обработчика ошибок
маршрута;useLoaderData с fetch внутри компонента,
чтобы избежать дублирования загрузки данных.useLoaderData в React Router обеспечивает удобный и
мощный способ предварительной загрузки данных, делая маршрутизацию и
работу с асинхронными ресурсами максимально гладкой и декларативной.