Индикаторы загрузки для маршрутов

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


Асинхронные элементы и loader

Каждый маршрут в TanStack Router может быть снабжен асинхронным loader — функцией, которая загружает необходимые данные до отображения компонента маршрута. Например:

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

const userRoute = createRoute({
  path: '/users/:userId',
  loader: async ({ params }) => {
    const response = await fetch(`/api/users/${params.userId}`);
    if (!response.ok) throw new Error('Не удалось загрузить данные пользователя');
    return response.json();
  },
  component: UserComponent,
});

При использовании loader роутер ожидает завершения загрузки данных перед рендером маршрута. Для отслеживания состояния загрузки применяется хук useMatch() или useRouteLoaderData().


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

Для визуализации процесса загрузки используется состояние маршрута, доступное через useMatch():

import { useMatch } from '@tanstack/router';

function UserComponent() {
  const match = useMatch('/users/:userId');

  if (match.status === 'loading') {
    return <div>Загрузка данных пользователя...</div>;
  }

  if (match.status === 'error') {
    return <div>Ошибка при загрузке: {match.error.message}</div>;
  }

  const user = match.data;
  return <div>Имя пользователя: {user.name}</div>;
}

Ключевые состояния маршрута:

  • loading – данные маршрута загружаются.
  • error – произошла ошибка при загрузке.
  • success – данные успешно загружены, компонент рендерится.

Использование этих состояний позволяет создавать динамические индикаторы загрузки, которые реагируют на асинхронные операции конкретного маршрута.


Глобальные индикаторы загрузки

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

import { router } from './router';

router.subscribe(({ isTransitioning }) => {
  const loader = document.getElementById('global-loader');
  if (isTransitioning) {
    loader.style.display = 'block';
  } else {
    loader.style.display = 'none';
  }
});

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


Ленивые компоненты и Suspense

TanStack Router тесно интегрируется с React Suspense. Если компонент маршрута загружается лениво, индикатор загрузки можно реализовать через <Suspense>:

import { lazy, Suspense } from 'react';

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

const userRoute = createRoute({
  path: '/users/:userId',
  component: () => (
    <Suspense fallback={<div>Загрузка компонента...</div>}>
      <LazyUserComponent />
    </Suspense>
  ),
});

Преимущества этого подхода:

  • Поддержка кода-сплиттинга.
  • Автоматическое отображение индикатора только до загрузки компонента.
  • Возможность комбинировать с loader для асинхронной загрузки данных.

Применение useIsFetching и оптимистическая загрузка

TanStack Router позволяет комбинировать данные маршрута с глобальными флагами загрузки данных. В случае асинхронных запросов к серверу можно использовать флаг isFetching для отображения индикатора без блокировки UI:

import { useRouteLoaderData, useIsFetching } from '@tanstack/router';

function AppLoader() {
  const isFetching = useIsFetching();

  return isFetching ? <div className="spinner">Загрузка...</div> : null;
}

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


Рекомендации по UX

  1. Минимальная задержка – спиннер появляется только если загрузка длится дольше 200–300 мс, чтобы избежать мерцания.
  2. Контекстные индикаторы – для больших страниц лучше отображать загрузку конкретного блока, а не всего экрана.
  3. Обработка ошибок – всегда комбинировать индикатор с состоянием ошибки для улучшения UX.

Использование этих подходов позволяет строить маршруты с чётко управляемой асинхронной загрузкой, информировать пользователя о состоянии данных и создавать отзывчивый интерфейс даже при сложной логике маршрутизации.