TanStack Router предоставляет гибкую систему маршрутизации, позволяющую строить сложные многоуровневые маршруты в приложениях на JavaScript. Одним из важных аспектов построения маршрутов является защита вложенных маршрутов, чтобы ограничить доступ к определённым страницам только для авторизованных пользователей или пользователей с конкретными правами.
Вложенные маршруты позволяют организовать маршруты в виде дерева.
Родительский маршрут может содержать дочерние, которые наследуют часть
поведения и состояния родителя. В TanStack Router это достигается через
объект маршрута с массивом children:
const routes = [
{
path: '/',
component: HomePage,
children: [
{
path: 'dashboard',
component: DashboardPage,
},
{
path: 'profile',
component: ProfilePage,
},
],
},
];
Каждый дочерний маршрут может быть защищён независимо, либо наследовать защиту родителя.
Для защиты маршрутов в TanStack Router используется механизм загрузчиков (loader) и хуков для проверки доступа. Наиболее распространённая стратегия — проверка авторизации перед рендерингом компонента:
import { redirect } from '@tanstack/router';
function authLoader() {
const user = getCurrentUser();
if (!user) {
throw redirect('/login');
}
return user;
}
В этом примере authLoader проверяет наличие
пользователя. Если пользователь не авторизован, выполняется
перенаправление на страницу входа. Этот loader можно подключить к
маршруту:
const routes = [
{
path: '/',
component: HomeLayout,
children: [
{
path: 'dashboard',
component: DashboardPage,
loader: authLoader,
},
{
path: 'profile',
component: ProfilePage,
loader: authLoader,
},
],
},
];
Чтобы не дублировать проверку авторизации на каждом дочернем маршруте, можно определить loader на уровне родителя. Все вложенные маршруты будут автоматически защищены:
const routes = [
{
path: '/',
component: HomeLayout,
loader: authLoader, // защита применяется ко всем детям
children: [
{
path: 'dashboard',
component: DashboardPage,
},
{
path: 'profile',
component: ProfilePage,
},
],
},
];
В этом случае проверка выполняется один раз при входе на любой дочерний маршрут.
Для более тонкой защиты можно реализовать проверку ролей пользователя:
function roleLoader(requiredRole) {
return () => {
const user = getCurrentUser();
if (!user || user.role !== requiredRole) {
throw redirect('/unauthorized');
}
return user;
};
}
Применение:
const routes = [
{
path: '/admin',
component: AdminLayout,
loader: roleLoader('admin'),
children: [
{
path: 'settings',
component: AdminSettingsPage,
},
],
},
];
Теперь доступ к /admin/settings будет только у
пользователей с ролью admin.
TanStack Router поддерживает ленивую загрузку компонентов, что позволяет совместить защиту маршрутов с оптимизацией загрузки:
import { lazy } from 'react';
const DashboardPage = lazy(() => import('./DashboardPage'));
const routes = [
{
path: '/dashboard',
component: DashboardPage,
loader: authLoader,
},
];
Loader выполняется до загрузки компонента, что предотвращает рендеринг защищённой страницы без авторизации.
Вложенные маршруты часто нуждаются в перенаправлениях при открытии родительского маршрута:
const routes = [
{
path: '/app',
component: AppLayout,
loader: authLoader,
children: [
{
path: '/',
redirect: 'dashboard', // при переходе на /app редирект на /app/dashboard
},
{
path: 'dashboard',
component: DashboardPage,
},
],
},
];
Такое решение сохраняет защиту маршрута и улучшает пользовательский опыт, автоматически направляя на корректную дочернюю страницу.
Для лучшего контроля над защитой можно использовать компонент
ErrorBoundary, чтобы отлавливать ошибки типа
redirect и показывать пользовательские страницы:
function AuthErrorBoundary({ error }) {
if (error.status === 302) {
return <RedirectPage />;
}
return <ErrorPage message={error.message} />;
}
const routes = [
{
path: '/dashboard',
component: DashboardPage,
loader: authLoader,
errorComponent: AuthErrorBoundary,
},
];
Это позволяет централизованно обрабатывать все ошибки доступа, включая редиректы и сообщения об отказе в доступе.
loader, чтобы исключить рендеринг
запрещённых компонентов.Такой подход обеспечивает безопасное управление вложенными маршрутами и позволяет строить масштабируемые приложения с чётким разграничением прав доступа.