React Router предоставляет встроенные механизмы для работы с асинхронными данными, в том числе возможность централизованной обработки ошибок, возникающих при загрузке данных в компонентах маршрутов. Основная идея заключается в использовании loader-функций и errorElement для перехвата и отображения ошибок без нарушения работы всего приложения.
Каждый маршрут может иметь функцию loader, которая
вызывается перед рендерингом компонента и отвечает за получение
необходимых данных. Loader возвращает промис, и если он завершается
успешно, данные передаются компоненту через хук
useLoaderData().
import { createBrowserRouter, RouterProvider, useLoaderData } from "react-router-dom";
async function userLoader({ params }) {
const response = await fetch(`/api/users/${params.userId}`);
if (!response.ok) {
throw new Response("Ошибка загрузки данных пользователя", { status: response.status });
}
return response.json();
}
function User() {
const user = useLoaderData();
return <div>{user.name}</div>;
}
const router = createBrowserRouter([
{
path: "/users/:userId",
element: <User />,
loader: userLoader,
},
]);
function App() {
return <RouterProvider router={router} />;
}
Ключевые моменты:
loader вызывается автоматически при переходе на
маршрут.loader можно перехватывать через
throw new Response(...) или обычный
throw new Error(...).loader, доступны только через
useLoaderData.Для отображения ошибок на уровне маршрута используется
errorElement. Это позволяет показывать пользователю
понятное сообщение без необходимости оборачивать каждый компонент в
try/catch.
function UserError({ error }) {
return (
<div>
<h2>Не удалось загрузить пользователя</h2>
<p>{error.statusText || error.message}</p>
</div>
);
}
const router = createBrowserRouter([
{
path: "/users/:userId",
element: <User />,
loader: userLoader,
errorElement: <UserError />,
},
]);
Особенности работы:
loader выбрасывает ошибку, React Router
автоматически рендерит errorElement.error, который
может содержать объект Response или стандартную ошибку
JavaScript.errorElement для разных
маршрутов, что обеспечивает гибкость и локализацию обработки
ошибок.Для крупных приложений удобно определять глобальный
обработчик ошибок на уровне родительского маршрута. Это
позволяет перехватывать ошибки всех дочерних маршрутов, не прописывая
errorElement для каждого маршрута отдельно.
function RootError({ error }) {
return (
<div>
<h1>Произошла ошибка</h1>
<p>{error.statusText || error.message}</p>
</div>
);
}
const router = createBrowserRouter([
{
path: "/",
element: <AppLayout />,
errorElement: <RootError />,
children: [
{ path: "users/:userId", element: <User />, loader: userLoader },
{ path: "posts/:postId", element: <Post />, loader: postLoader },
],
},
]);
Преимущества:
React Router поддерживает асинхронные маршруты с помощью
defer и Suspense. Это особенно важно при
загрузке больших объемов данных. Ошибки внутри defer также
могут быть перехвачены через errorElement.
import { defer, Await } from "react-router-dom";
function userLoader({ params }) {
return defer({
user: fetch(`/api/users/${params.userId}`).then(res => {
if (!res.ok) throw new Error("Ошибка загрузки");
return res.json();
}),
});
}
function User() {
const { user } = useLoaderData();
return (
<Suspense fallback={<p>Загрузка...</p>}>
<Await resolve={user}>
{(data) => <div>{data.name}</div>}
</Await>
</Suspense>
);
}
Особенности:
defer позволяет рендерить компонент до завершения
полной загрузки данных.Await, будут направлены в
ближайший errorElement.Suspense с Await
обеспечивает плавный пользовательский интерфейс без зависаний.throw new Response(...)
для корректного задания статуса HTTP и сообщения.errorElement
комбинировать для разных уровней маршрутов.errorElement.Эти механизмы делают React Router мощным инструментом для надежной загрузки данных и устойчивого управления ошибками в современных SPA-приложениях.