loader в React Router — это функция, которая асинхронно
загружает данные перед рендерингом компонента маршрута. Она позволяет
реализовать data fetching на уровне маршрутов и возвращать
данные напрямую в компонент через хук useLoaderData. Однако
при работе с внешними API, базами данных или другими асинхронными
источниками данных всегда существует вероятность возникновения ошибок.
React Router предоставляет встроенные механизмы для их обработки.
throw для генерации ошибокВнутри loader можно использовать throw для
передачи ошибок маршруту:
import { json } from "react-router-dom";
export async function loader({ params }) {
try {
const response = await fetch(`/api/items/${params.id}`);
if (!response.ok) {
throw json({ message: "Элемент не найден" }, { status: 404 });
}
return response.json();
} catch (error) {
throw json({ message: error.message }, { status: 500 });
}
}
Ключевые моменты:
throw json(data, options) — встроенный метод React
Router для передачи ошибки с HTTP-статусом.status используется для корректного отображения ошибки
на клиенте и возможности перехвата её через
errorElement.errorElement
и обработка ошибок на уровне маршрутаКаждый маршрут может иметь свой errorElement —
компонент, который рендерится при возникновении ошибки в
loader или в компоненте маршрута:
import { useRouteError } from "react-router-dom";
function ItemError() {
const error = useRouteError();
return (
<div>
<h2>Ошибка загрузки элемента</h2>
<p>{error.status}: {error.data?.message || error.message}</p>
</div>
);
}
const routes = [
{
path: "/items/:id",
element: <ItemPage />,
loader: loader,
errorElement: <ItemError />,
}
];
Особенности:
useRouteError() возвращает объект ошибки, брошенной в
loader или компоненте.status и data (если
используется throw json) или message для
стандартных исключений.loader может возвращать промисы, которые могут быть
отклонены. React Router корректно обрабатывает такие случаи и передает
отклонённую ошибку в errorElement. Пример с асинхронной
функцией:
export async function loader({ params }) {
const response = await fetch(`/api/items/${params.id}`);
if (!response.ok) {
throw new Response("Not Found", { status: 404 });
}
return response.json();
}
Здесь throw new Response создаёт нативный объект ответа,
который React Router автоматически распознает и передаст в
useRouteError().
deferДля маршрутов с defer данные загружаются лениво, и
ошибки могут возникать на отдельных промисах. Их можно перехватывать
через Await:
import { defer, Await, useLoaderData } from "react-router-dom";
export function loader() {
return defer({
item: fetchItem().catch(err => { throw err; })
});
}
function ItemPage() {
const data = useLoaderData();
return (
<Suspense fallback={<div>Загрузка...</div>}>
<Await resolve={data.item} errorElement={<div>Ошибка при загрузке</div>}>
{(item) => <div>{item.name}</div>}
</Await>
</Suspense>
);
}
Ключевые моменты:
defer позволяет рендерить компонент до завершения
загрузки всех данных.Await обеспечивает обработку ошибок конкретного промиса
отдельно.errorElement в Await работает аналогично
маршруту.Для обработки всех ошибок приложения можно использовать корневой
маршрут с errorElement. Это позволяет централизованно
показывать сообщения о сбоях:
function RootError() {
const error = useRouteError();
return (
<div>
<h1>Произошла ошибка</h1>
<p>{error.status || 500}: {error.statusText || error.message}</p>
</div>
);
}
const router = createBrowserRouter([
{
path: "/",
element: <RootLayout />,
errorElement: <RootError />,
children: [
{ path: "items/:id", element: <ItemPage />, loader: loader },
]
}
]);
Преимущество глобальной обработки — единый интерфейс для ошибок, не зависящий от конкретного маршрута, с возможностью кастомизации вида страницы.
throw json или
throw Response для ошибок, которые должны
отображаться пользователю.errorElement на уровне
маршрута для изоляции ошибок конкретного маршрута.defer, оборачивая возможные ошибки в catch или
throw.message, status) для информативного
отображения.Эти подходы делают загрузку данных через loader
безопасной и позволяют создавать устойчивый пользовательский интерфейс,
корректно реагирующий на ошибки API или непредвиденные сбои при
асинхронных запросах.