В React Router loader представляет собой асинхронную
функцию, предназначенную для предварительной загрузки данных перед
рендерингом маршрута. Эта концепция позволяет отделять процесс получения
данных от компонентов, делая их более предсказуемыми и тестируемыми.
loader определяется на уровне маршрута и вызывается
автоматически при переходе на соответствующий путь. Он должен возвращать
данные, которые затем доступны через хук useLoaderData
внутри компонента маршрута.
Пример базовой структуры маршрута с loader:
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import App from "./App";
import UserPage from "./UserPage";
const router = createBrowserRouter([
{
path: "/users/:userId",
element: <UserPage />,
loader: async ({ params }) => {
const response = await fetch(`https://api.example.com/users/${params.userId}`);
if (!response.ok) throw new Response("User not found", { status: 404 });
return response.json();
},
},
]);
function Root() {
return <RouterProvider router={router} />;
}
В данном примере loader получает объект
params, содержащий параметры маршрута
(userId), и возвращает результат запроса к API.
Функция loader получает один объект с несколькими
ключами:
:userId, :postId и т.д.)Request, аналогичный
стандартному Fetch API, предоставляющий информацию о текущем
запросеПример использования request и context:
loader: async ({ params, request, context }) => {
const token = context.authToken;
const response = await fetch(`https://api.example.com/users/${params.userId}`, {
headers: { Authorization: `Bearer ${token}` },
});
return response.json();
}
После того как loader возвращает данные, их можно
получить с помощью хука useLoaderData:
import { useLoaderData } from "react-router-dom";
function UserPage() {
const user = useLoaderData();
return (
<div>
<h1>{user.name}</h1>
<p>Email: {user.email}</p>
</div>
);
}
useLoaderData гарантирует, что компонент получает уже
загруженные данные, исключая необходимость дополнительного состояния и
useEffect для запроса данных.
Если запрос в loader завершается ошибкой или
возвращается Response с кодом ошибки, React Router позволяет
обрабатывать такие случаи через компонент ErrorBoundary или
errorElement маршрута.
Пример:
{
path: "/users/:userId",
element: <UserPage />,
loader: async ({ params }) => {
const response = await fetch(`https://api.example.com/users/${params.userId}`);
if (!response.ok) throw new Response("User not found", { status: 404 });
return response.json();
},
errorElement: <UserErrorPage />,
}
В компоненте UserErrorPage можно получить объект ошибки
через хук useRouteError:
import { useRouteError } from "react-router-dom";
function UserErrorPage() {
const error = useRouteError();
return <div>Ошибка: {error.statusText || "Неизвестная ошибка"}</div>;
}
При сложных маршрутах с вложенными маршрутами loader
каждого уровня вызывается независимо, но React Router позволяет
загружать данные последовательно или параллельно:
loader может инициировать перенаправление с помощью
утилиты redirect из react-router-dom:
import { redirect } from "react-router-dom";
loader: async ({ params }) => {
const isAuthorized = await checkAuth();
if (!isAuthorized) return redirect("/login");
return fetchUserData(params.userId);
}
Это позволяет управлять логикой доступа до рендеринга компонента, предотвращая кратковременное отображение неавторизованного контента.
React Router 6.10+ поддерживает использование loader
вместе с Suspense. Если loader возвращает
Promise, маршрутизатор может отобразить fallback UI до
завершения загрузки данных:
const router = createBrowserRouter([
{
path: "/dashboard",
element: (
<React.Suspense fallback={<div>Загрузка...</div>}>
<Dashboard />
</React.Suspense>
),
loader: async () => fetch("/api/dashboard").then(res => res.json()),
},
]);
Это повышает отзывчивость интерфейса и обеспечивает плавное отображение данных.
useLoaderData.Loader в React Router превращает маршруты в мощные точки контроля загрузки данных, делая структуру приложения более декларативной и предсказуемой.