Отслеживание состояния загрузки

В TanStack Router управление состоянием загрузки (loading state) является одним из ключевых аспектов построения отзывчивых и предсказуемых интерфейсов. Библиотека предоставляет встроенные механизмы для отслеживания загрузки данных на уровне маршрутов и компонентов, что позволяет создавать сложные асинхронные сценарии без лишнего кода.

Структура маршрута и загрузка данных

Каждый маршрут в TanStack Router может иметь функцию loader, которая отвечает за асинхронное получение данных перед рендерингом компонента. Функция loader возвращает промис, а TanStack Router автоматически отслеживает состояние этого промиса.

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

const rootRoute = createRouteConfig({
  path: '/',
  component: RootComponent,
});

const userRoute = rootRoute.createRoute({
  path: 'user/:userId',
  component: UserComponent,
  loader: async ({ params }) => {
    const response = await fetch(`/api/users/${params.userId}`);
    if (!response.ok) {
      throw new Error('Failed to load user');
    }
    return response.json();
  },
});

const router = createRouter({ routeConfig: rootRoute });

В этом примере loader маршрута user/:userId выполняет асинхронный запрос к API. Пока промис не завершён, маршрут находится в состоянии загрузки.

Использование useIsLoading и useMatch

Для отслеживания состояния загрузки компонентов и маршрутов TanStack Router предоставляет хуки useIsLoading и useMatch.

  • useIsLoading позволяет проверить, выполняется ли в данный момент загрузка данных для конкретного маршрута.
  • useMatch позволяет получить состояние конкретного маршрута и информацию о его loader.
import { useIsLoading, useMatch } from '@tanstack/router';

function LoadingIndicator() {
  const isLoading = useIsLoading({ from: rootRoute });
  return isLoading ? <div>Загрузка...</div> : null;
}

function UserComponent() {
  const match = useMatch(userRoute);
  const user = match.data;

  return (
    <div>
      {match.isLoading ? 'Загрузка данных пользователя...' : `Имя: ${user.name}`}
    </div>
  );
}

useIsLoading автоматически отслеживает все активные loader маршрутов, указанных в параметре from. useMatch предоставляет объект с полями:

  • data — данные, возвращённые loader;
  • isLoading — булевое значение состояния загрузки;
  • error — объект ошибки, если промис loader завершился с ошибкой.

Глобальное и локальное управление загрузкой

TanStack Router поддерживает как глобальные, так и локальные индикаторы загрузки.

  • Глобальный индикатор используется для отображения состояния загрузки для всего приложения. Обычно это реализуется через компонент, который подписан на useIsLoading без конкретного from:
function GlobalLoading() {
  const isLoading = useIsLoading();
  return isLoading ? <div className="global-spinner">Загрузка приложения...</div> : null;
}
  • Локальный индикатор применяется для конкретного маршрута или компонента:
function UserDetails() {
  const match = useMatch(userRoute);
  if (match.isLoading) return <div>Загрузка информации о пользователе...</div>;
  return <div>{match.data.name}</div>;
}

Асинхронные действия и их взаимодействие с состоянием загрузки

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

const editUserRoute = userRoute.createRoute({
  path: 'edit',
  component: EditUser,
  action: async ({ params, formData }) => {
    const response = await fetch(`/api/users/${params.userId}`, {
      method: 'POST',
      body: formData,
    });
    if (!response.ok) {
      throw new Error('Ошибка при обновлении пользователя');
    }
    return response.json();
  },
});

Компоненты, использующие useMatch или useIsLoading, автоматически реагируют на состояние загрузки action. Таким образом, можно показывать спиннеры, блокировать кнопки или информировать пользователя о процессе выполнения асинхронной операции.

Оптимизация UX при загрузке

  1. Skeleton Loading — отображение заглушек контента вместо пустых областей. TanStack Router позволяет использовать состояние isLoading для условного рендеринга skeleton-компонентов.
  2. Приоритизация загрузки — использование параметра from в useIsLoading позволяет отслеживать загрузку только для конкретных маршрутов, что снижает количество ненужных обновлений UI.
  3. Обработка ошибок — сочетание loader и errorBoundary позволяет корректно реагировать на ошибки загрузки, не ломая структуру приложения.

Примеры продвинутого использования

  • Параллельная загрузка данных нескольких маршрутов
const dashboardRoute = rootRoute.createRoute({
  path: 'dashboard',
  component: Dashboard,
  loader: async () => {
    const [stats, notifications] = await Promise.all([
      fetch('/api/stats').then(res => res.json()),
      fetch('/api/notifications').then(res => res.json()),
    ]);
    return { stats, notifications };
  },
});
  • Комбинирование локальных и глобальных индикаторов
function App() {
  return (
    <>
      <GlobalLoading />
      <RouterProvider router={router} />
    </>
  );
}

Сочетание этих методов обеспечивает гибкую систему управления состоянием загрузки для сложных SPA-приложений. TanStack Router обеспечивает прозрачную интеграцию асинхронных процессов с интерфейсом, позволяя строить предсказуемый UX без лишней логики управления промисами.