В TanStack Router pending-состояние отражает процесс ожидания завершения асинхронных операций, связанных с навигацией. Оно используется для контроля загрузки данных, отображения индикаторов загрузки и предотвращения преждевременного отображения контента. Правильное управление этим состоянием позволяет создавать плавные пользовательские интерфейсы без “мерцания” или неконсистентных данных.
В TanStack Router управление pending реализуется через следующие ключевые концепции:
Загрузчики (loader) Каждому
маршруту можно присвоить функцию загрузки данных, которая возвращает
Promise. Пока Promise не выполнен, маршрут считается в состоянии
pending.
import { createRouter, createRouteConfig } from '@tanstack/router';
const route = createRouteConfig()
.path('/users/:id')
.loader(async ({ params }) => {
const response = await fetch(`/api/users/${params.id}`);
return response.json();
});
Здесь loader автоматически инициирует pending-состояние
при вызове и завершает его после выполнения Promise.
Глобальные индикаторы загрузки TanStack Router предоставляет доступ к состоянию маршрута и его загрузчиков через useIsFetching или usePending. Это позволяет строить глобальные индикаторы загрузки:
import { usePending } from '@tanstack/router';
function LoadingIndicator() {
const isPending = usePending();
return isPending ? <div>Загрузка...</div> : null;
}
В отличие от простого флага, usePending отслеживает все
текущие асинхронные операции маршрутизатора, включая переходы и загрузки
данных.
Переходные состояния (transition)
При навигации TanStack Router создаёт объект перехода, который
отражает:
Использование transition позволяет синхронизировать UI с жизненным циклом маршрута:
import { useTransition } from '@tanstack/router';
function Page() {
const transition = useTransition();
return (
<div>
{transition.state === 'pending' && <span>Загрузка страницы...</span>}
</div>
);
}TanStack Router поддерживает иерархические маршруты, где один маршрут может содержать подмаршруты. Важно понимать, что pending-состояние каждого уровня маршрута можно отслеживать отдельно:
const usersRoute = createRouteConfig().path('/users').loader(fetchUsers);
const userDetailsRoute = usersRoute.createRoute().path(':id').loader(fetchUserById);
/users/1 сначала сработает pending на
usersRoute, затем на userDetailsRoute.usePending(route), где route
— конкретный объект маршрута.import { usePending } from '@tanstack/router';
function UserDetails() {
const isPending = usePending(userDetailsRoute);
return isPending ? <div>Загрузка данных пользователя...</div> : <UserDetailsContent />;
}
TanStack Router позволяет конфигурировать несколько аспектов:
Задержка отображения индикатора Чтобы избежать
мерцания индикатора при мгновенной загрузке, можно использовать
pendingMs:
const router = createRouter({
routeConfig,
pendingMs: 200, // Показывать индикатор только если загрузка > 200мс
});Стратегии отмены запросов Асинхронные загрузчики можно отменять при навигации, чтобы не загружать устаревшие данные:
.loader(async ({ params, signal }) => {
const response = await fetch(`/api/users/${params.id}`, { signal });
return response.json();
});
Использование AbortController позволяет автоматически
завершить предыдущие запросы при изменении маршрута.
TanStack Router интегрируется с React Suspense для асинхронной загрузки компонентов:
const route = createRouteConfig()
.path('/profile')
.element(
<React.Suspense fallback={<div>Загрузка профиля...</div>}>
<Profile />
</React.Suspense>
);
pendingMs или debounce для глобальных индикаторов.signal для отмены:
предотвращает загрузку устаревших данных при быстрых навигациях.Это позволяет создавать детерминированное, предсказуемое управление состоянием загрузки в сложных приложениях с большим количеством маршрутов и асинхронных данных.