Перехват маршрутов (route interception) в TanStack
Router предоставляет возможность контролировать переходы между
страницами на уровне маршрутизатора. Это особенно важно при реализации
логики аутентификации, подтверждения действий пользователя,
динамического редиректа или загрузки данных до рендера компонента.
TanStack Router позволяет вмешиваться в процесс навигации с помощью
функции beforeLoad и хуков маршрута.
beforeLoadbeforeLoad — это асинхронная функция, которая вызывается
перед тем, как маршрут будет загружен. Она может блокировать переход,
изменить маршрут или прервать навигацию.
Пример структуры маршрута с beforeLoad:
import { createRouter, createRoute } from '@tanstack/router';
const dashboardRoute = createRoute({
path: '/dashboard',
component: DashboardPage,
beforeLoad: async ({ context, params, router }) => {
const user = await context.getCurrentUser();
if (!user) {
// Отменяем переход
return router.navigate({ to: '/login' });
}
},
});
Ключевые моменты:
context — общий объект с данными, доступный всем
маршрутам.params — параметры маршрута.router — экземпляр маршрутизатора, через который можно
инициировать навигацию.Если beforeLoad возвращает редирект, текущий переход
отменяется, и выполняется новый маршрут. Если функция ничего не
возвращает, переход продолжается.
beforeExitДля контроля покидания маршрута используется beforeExit.
Он полезен, когда нужно предотвратить потерю несохранённых данных:
const editProfileRoute = createRoute({
path: '/profile/edit',
component: EditProfilePage,
beforeExit: ({ router, context }) => {
if (context.hasUnsavedChanges) {
return window.confirm('Есть несохранённые изменения. Продолжить?');
}
},
});
Особенности:
beforeExit вызывается перед уходом с маршрута, а не при
входе.true — разрешить,
false — блокировать) или Promise.Кроме локальных перехватчиков на маршруте, TanStack Router позволяет создавать глобальные перехватчики на уровне всего роутера:
const router = createRouter({
routeTree: rootRoute,
beforeNavigate: async ({ to, from }) => {
if (to.path.startsWith('/admin') && !await isAdminUser()) {
return '/login'; // Редирект на страницу логина
}
},
});
Особенности:
beforeNavigate срабатывает перед каждой
навигацией.'/login') инициирует редирект.false, чтобы полностью заблокировать
переход.Перехватчики могут использоваться совместно, создавая многоуровневую защиту:
beforeNavigate проверяет права доступа на
все страницы.beforeLoad на конкретном маршруте проверяет локальные
условия.beforeExit предотвращает потерю данных.Порядок выполнения:
beforeNavigate.beforeExit текущего маршрута.beforeLoad целевого маршрута.Если любой из них возвращает редирект или отмену, следующие шаги не выполняются.
beforeLoad часто используют для подгрузки данных
перед рендером:
const userRoute = createRoute({
path: '/users/:id',
component: UserPage,
beforeLoad: async ({ params }) => {
const userData = await fetchUser(params.id);
if (!userData) {
return '/404';
}
return { initialData: userData };
},
});
Возврат объекта из beforeLoad можно использовать для
передачи начальных данных в компонент. Это позволяет избегать состояния
загрузки внутри самого компонента и обеспечивает предзагрузку данных на
уровне маршрутизатора.
beforeLoad, чтобы маршрут не
рендерился до получения информации.beforeExit.router.navigateРедирект в beforeLoad или beforeExit
осуществляется через router.navigate или возвращение строки
пути:
beforeLoad: async ({ router }) => {
if (!await isLoggedIn()) {
return router.navigate({ to: '/login' });
}
}
Альтернатива — вернуть путь напрямую:
beforeLoad: async () => {
if (!await isLoggedIn()) {
return '/login';
}
}
Оба подхода одинаково корректны, но возвращение строки короче и читабельнее для простых случаев.
Global beforeNavigate -> Current Route beforeExit -> Target Route beforeLoad
Каждый уровень может:
Эта гибкая архитектура позволяет строить сложные маршрутизационные сценарии, минимизируя дублирование кода и обеспечивая централизованный контроль над навигацией.