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>;
}
Ключевые состояния маршрута:
Использование этих состояний позволяет создавать динамические индикаторы загрузки, которые реагируют на асинхронные операции конкретного маршрута.
Для крупных приложений часто требуется глобальный
индикатор, который активируется при любом переходе между
маршрутами. 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';
}
});
Таким образом, можно управлять отображением единого спиннера на уровне всего приложения, не прописывая индикаторы для каждого маршрута вручную.
SuspenseTanStack 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;
}
Это позволяет создать неблокирующую загрузку, при которой пользователь видит интерфейс сразу, а индикатор сообщает о продолжающемся процессе загрузки данных.
Использование этих подходов позволяет строить маршруты с чётко управляемой асинхронной загрузкой, информировать пользователя о состоянии данных и создавать отзывчивый интерфейс даже при сложной логике маршрутизации.