Intercepting routes

Перехват маршрутов (route interception) в TanStack Router предоставляет возможность контролировать переходы между страницами на уровне маршрутизатора. Это особенно важно при реализации логики аутентификации, подтверждения действий пользователя, динамического редиректа или загрузки данных до рендера компонента. TanStack Router позволяет вмешиваться в процесс навигации с помощью функции beforeLoad и хуков маршрута.


Основные механизмы перехвата

beforeLoad

beforeLoad — это асинхронная функция, которая вызывается перед тем, как маршрут будет загружен. Она может блокировать переход, изменить маршрут или прервать навигацию.

Пример структуры маршрута с 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, чтобы полностью заблокировать переход.

Комбинация перехватчиков

Перехватчики могут использоваться совместно, создавая многоуровневую защиту:

  1. Глобальный beforeNavigate проверяет права доступа на все страницы.
  2. beforeLoad на конкретном маршруте проверяет локальные условия.
  3. beforeExit предотвращает потерю данных.

Порядок выполнения:

  1. Глобальные beforeNavigate.
  2. beforeExit текущего маршрута.
  3. 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.
  • Всегда учитывайте асинхронность: TanStack Router корректно обрабатывает промисы, позволяя организовать цепочки проверок и редиректов без сложного состояния внутри компонентов.

Перехват и редирект через 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

Каждый уровень может:

  • Разрешить переход.
  • Заблокировать переход.
  • Выполнить редирект на другой маршрут.
  • Передать данные в целевой компонент.

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