useAsyncValue и useAsyncError

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


useAsyncValue

useAsyncValue используется для получения результата асинхронного загрузчика маршрута (loader). Он возвращает значение, которое было возвращено из функции загрузки маршрута после её успешного выполнения. Этот хук облегчает работу с данными на уровне компонентов, связанных с маршрутом, устраняя необходимость самостоятельно управлять состоянием загрузки и ошибок.

Пример использования:

import { useAsyncValue } from "react-router-dom";

function UserProfile() {
  const user = useAsyncValue();

  return (
    <div>
      <h1>Профиль пользователя</h1>
      <p>Имя: {user.name}</p>
      <p>Email: {user.email}</p>
    </div>
  );
}

В этом примере user содержит данные, загруженные асинхронным loader для маршрута, связанного с компонентом UserProfile. Важно помнить, что useAsyncValue автоматически блокирует рендер до завершения загрузки данных, поэтому компонент получает готовый объект без необходимости проверять состояние загрузки.

Особенности:

  • useAsyncValue работает только внутри компонентов, вложенных в маршруты, где определен loader.
  • Если загрузка данных завершилась с ошибкой, useAsyncValue не возвращает значение — вместо этого ошибка передается в систему обработки через useAsyncError или родительский ErrorBoundary.
  • Позволяет строить вложенные маршруты с отдельными асинхронными данными для каждого уровня.

useAsyncError

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

Пример использования:

import { useAsyncError } from "react-router-dom";

function UserErrorBoundary() {
  const error = useAsyncError();

  return (
    <div>
      <h2>Ошибка загрузки данных пользователя</h2>
      <p>{error.message}</p>
    </div>
  );
}

В этом примере, если асинхронный loader маршрута вызывает исключение, компонент UserErrorBoundary сможет отобразить сообщение об ошибке. Это позволяет изолировать ошибки по маршрутам и предотвращает падение всего приложения из-за одной неудачной загрузки данных.

Особенности:

  • Работает только внутри маршрутов, где определен loader или action, способный выбрасывать ошибки.
  • Позволяет строить комплексные стратегии обработки ошибок, включая кастомные сообщения, повторные попытки загрузки или редиректы.
  • Может быть использован совместно с React Router ErrorBoundary для обработки ошибок на уровне целых веток маршрутов.

Взаимодействие с загрузчиками (loader)

Для полноценного использования этих хуков необходимо правильно настроить асинхронные загрузчики маршрутов. loader — это функция, которая выполняется перед рендерингом компонента маршрута и возвращает промис с данными или выбрасывает ошибку.

Пример loader для маршрута пользователя:

async function userLoader({ params }) {
  const response = await fetch(`/api/users/${params.userId}`);
  if (!response.ok) {
    throw new Error("Не удалось загрузить данные пользователя");
  }
  return response.json();
}

Здесь userLoader возвращает промис, который разрешается в объект пользователя. Если запрос неудачен, выбрасывается ошибка, которая затем обрабатывается через useAsyncError.

Маршрут с loader и компонентами:

import { createBrowserRouter, RouterProvider } from "react-router-dom";

const router = createBrowserRouter([
  {
    path: "/users/:userId",
    element: <UserProfile />,
    loader: userLoader,
    errorElement: <UserErrorBoundary />,
  },
]);

export default function App() {
  return <RouterProvider router={router} />;
}

В этом примере маршруты полностью используют асинхронные хуки:

  • useAsyncValue получает данные из userLoader.
  • useAsyncError используется в UserErrorBoundary для обработки ошибок загрузки.

Практические рекомендации

  1. Разделение загрузки и рендера: всегда разделять компоненты, отображающие данные, и компоненты для обработки ошибок. Это повышает читаемость и упрощает поддержку.
  2. Кастомные сообщения ошибок: использовать useAsyncError для создания специфичных для маршрута сообщений об ошибках.
  3. Вложенные маршруты: каждый уровень маршрута может иметь свой loader и свои хуки useAsyncValue и useAsyncError, что позволяет строить сложные приложения с изолированной загрузкой данных.
  4. Типизация: при использовании TypeScript желательно явно указывать тип данных, возвращаемых из loader, чтобы useAsyncValue сразу возвращал корректный тип.

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