Обработка ошибок загрузки данных

В TanStack Router управление состоянием маршрутов тесно связано с загрузкой данных через loader функции. При работе с асинхронными запросами неизбежно возникают ошибки: сетевые сбои, некорректные ответы API, проблемы с авторизацией. TanStack Router предоставляет инструменты для централизованной и удобной обработки таких ошибок, позволяя отображать пользователю корректные состояния без ломки приложения.

Loader и его поведение при ошибках

Каждый маршрут может содержать loader — асинхронную функцию, которая возвращает данные для компонента маршрута:

import { createRouter, createRouteConfig } from '@tanstack/router';

const userRoute = {
  path: '/users/:id',
  loader: async ({ params }) => {
    const response = await fetch(`/api/users/${params.id}`);
    if (!response.ok) {
      throw new Error(`Ошибка загрузки пользователя: ${response.status}`);
    }
    return response.json();
  }
};

Если loader выбрасывает исключение, TanStack Router автоматически передает управление в ближайший обработчик ошибок маршрута (errorComponent). Исключения могут быть как синхронными (throw new Error()), так и асинхронными (отклоненный Promise).

Error Boundary для маршрутов

TanStack Router поддерживает концепцию error boundaries, аналогичную React. Каждый маршрут может содержать errorComponent:

const UserError = ({ error }) => (
  <div>
    <h2>Не удалось загрузить пользователя</h2>
    <p>{error.message}</p>
  </div>
);

const userRoute = {
  path: '/users/:id',
  loader: async ({ params }) => { /* ... */ },
  errorComponent: UserError
};

Если loader выбросит ошибку, TanStack Router рендерит UserError вместо основного компонента. Ошибка передается через проп error, что позволяет точно отобразить причину сбоя.

Централизованная обработка ошибок

Для сложных приложений удобно иметь глобальный обработчик ошибок, который реагирует на все ошибки маршрутов. Это можно реализовать через родительский маршрут с errorComponent:

const RootError = ({ error }) => (
  <div>
    <h1>Произошла ошибка</h1>
    <p>{error.message}</p>
  </div>
);

const rootRoute = {
  path: '/',
  children: [userRoute],
  errorComponent: RootError
};

Ошибки дочерних маршрутов будут всплывать к родительскому errorComponent, если у дочернего маршрута нет собственного обработчика. Это позволяет централизовать логику отображения ошибок.

Обработка различных типов ошибок

Ошибки могут быть классифицированы по типу:

  • Сетевые ошибки — сбой запроса, недоступность сервера.
  • Ошибки авторизации — код ответа 401/403.
  • Ошибки валидации данных — некорректный формат ответа API.

Для различения ошибок можно использовать кастомные классы:

class NetworkError extends Error {}
class AuthError extends Error {}

const loader = async () => {
  const res = await fetch('/api/data');
  if (res.status === 401) throw new AuthError('Необходима авторизация');
  if (!res.ok) throw new NetworkError('Ошибка сети');
  return res.json();
};

В errorComponent можно проверять тип ошибки и рендерить разные состояния:

const DataError = ({ error }) => {
  if (error instanceof AuthError) return <div>Требуется вход</div>;
  if (error instanceof NetworkError) return <div>Проблемы с сетью</div>;
  return <div>Произошла неизвестная ошибка</div>;
};

Перезапрос данных после ошибки

TanStack Router поддерживает механизм повторной загрузки данных через функцию retry. Например, можно предоставить пользователю кнопку «Попробовать снова»:

const UserError = ({ error, retry }) => (
  <div>
    <p>{error.message}</p>
    <button onCl ick={retry}>Попробовать снова</button>
  </div>
);

Вызов retry повторно запускает loader, позволяя восстановить данные без перезагрузки всего приложения.

Асинхронные зависимости и комбинирование с Suspense

При работе с асинхронными данными можно объединять loader маршрутов с React Suspense. TanStack Router позволяет интегрировать асинхронные компоненты и обрабатывать ошибки на уровне маршрутов:

const UserComponent = React.lazy(() => import('./UserComponent'));

const userRoute = {
  path: '/users/:id',
  loader: async ({ params }) => fetch(`/api/users/${params.id}`).then(r => r.json()),
  component: () => (
    <React.Suspense fallback={<div>Загрузка...</div>}>
      <UserComponent />
    </React.Suspense>
  ),
  errorComponent: UserError
};

Ошибки при динамическом импорте также будут перехвачены errorComponent, что обеспечивает единообразную обработку.

Логирование и аналитика ошибок

Для продакшн-приложений рекомендуется отправлять ошибки в систему логирования или аналитики. Это делается в errorComponent или через глобальный хендлер:

const RootError = ({ error }) => {
  console.error(error);
  // Можно отправить в Sentry, LogRocket и т.п.
  return <div>Произошла ошибка. Попробуйте позже.</div>;
};

Поддержка асинхронного логирования позволяет собирать статистику по типам ошибок и местам их возникновения.

Резюме по обработке ошибок загрузки данных

  • loader маршрута выбрасывает ошибки при сбое загрузки данных.
  • errorComponent маршрута отображает ошибку и может предоставлять пользовательские действия (retry, переход).
  • Ошибки дочерних маршрутов всплывают к родительскому errorComponent при отсутствии локального обработчика.
  • Разделение типов ошибок и кастомные классы позволяют создавать адаптивные интерфейсы.
  • Интеграция с Suspense и логированием делает обработку ошибок универсальной и устойчивой.

Эта модель обработки ошибок обеспечивает стабильность приложений на TanStack Router, позволяя надежно работать с асинхронными данными и предотвращать разрывы пользовательского интерфейса.