Transitions между маршрутами

TanStack Router предоставляет гибкую и мощную систему управления переходами между маршрутами в приложениях на JavaScript, обеспечивая плавное и предсказуемое поведение при навигации. В основе механизма переходов лежит система хуков и событий, которые позволяют перехватывать, модифицировать или отменять переход между маршрутами.


Основы переходов

Переход (transition) в TanStack Router — это процесс изменения текущего маршрута на другой. Он может быть синхронным или асинхронным, в зависимости от того, требуется ли предварительная загрузка данных, проверка прав доступа или выполнение анимаций.

Ключевые элементы переходов:

  • navigate() — основной метод для программной навигации. Принимает путь маршрута или объект с параметрами маршрута.
  • route.loader() — асинхронная функция, которая выполняется перед тем, как маршрут станет активным, позволяя загружать данные или выполнять проверки.
  • route.onEnter() / route.onExit() — хуки, вызываемые при входе на маршрут и при выходе из него соответственно.
  • transitionState — объект, который описывает текущее состояние перехода, например idle, loading, submitting, error.

Пример базового перехода с использованием navigate():

import { useRouter } from '@tanstack/router'

const router = useRouter()

function goToProfile(userId) {
  router.navigate({ to: '/profile', params: { id: userId } })
}

Асинхронные переходы и загрузка данных

Одной из ключевых особенностей TanStack Router является поддержка асинхронной загрузки данных перед переходом. Это достигается через loader и onLoad:

const profileRoute = router.createRoute({
  path: '/profile/:id',
  loader: async ({ params }) => {
    const response = await fetch(`/api/users/${params.id}`)
    if (!response.ok) throw new Error('Failed to load profile')
    return response.json()
  },
  component: ProfilePage,
})

В этом примере переход на маршрут /profile/:id будет ожидать завершения загрузки данных перед рендерингом компонента. Если loader выбрасывает ошибку, переход автоматически переходит в состояние error.


Контроль переходов через хуки

TanStack Router предоставляет несколько хуков для управления переходами:

  • onBeforeUnload — вызывается перед уходом с текущего маршрута. Позволяет отменять переход или показывать предупреждение.
  • onEnter — выполняется при входе на маршрут. Часто используется для проверки авторизации.
  • onExit — выполняется при выходе с маршрута. Полезно для очистки ресурсов или сохранения состояния.

Пример использования onBeforeUnload:

const editRoute = router.createRoute({
  path: '/edit/:id',
  onBeforeUnload: ({ next }) => {
    if (unsavedChangesExist()) {
      return confirm('У вас есть несохраненные изменения. Продолжить?')
        ? true
        : false
    }
    return true
  },
  component: EditPage,
})

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


Анимации переходов

Для визуальных эффектов TanStack Router не содержит встроенной системы анимаций, но позволяет интегрировать сторонние библиотеки (например, Framer Motion или React Transition Group) через transitionState:

import { useTransition, useRouter } from '@tanstack/router'

const router = useRouter()
const transition = useTransition()

return (
  <div>
    {transition.state === 'loading' && <LoadingSpinner />}
    <AnimatedRoutes key={router.currentRoute.id}>
      <RouterView />
    </AnimatedRoutes>
  </div>
)

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


Переходы с блокировкой

Иногда необходимо временно заблокировать переход, например, при асинхронной валидации или сохранении формы. Для этого используется объект transition с методами pause() и resume():

router.addTransitionHook(async (transition) => {
  if (needsValidation()) {
    transition.pause()
    await validateData()
    transition.resume()
  }
})

Такой подход обеспечивает точный контроль над тем, когда переход продолжается, предотвращая потерю данных или некорректное состояние приложения.


Обработка ошибок при переходах

TanStack Router предоставляет централизованную обработку ошибок через errorBoundary на уровне маршрута или глобально:

const dashboardRoute = router.createRoute({
  path: '/dashboard',
  loader: async () => {
    throw new Error('Failed to load dashboard')
  },
  errorBoundary: ({ error }) => <ErrorComponent message={error.message} />,
  component: DashboardPage,
})

Любая ошибка в loader, onEnter или других хуках автоматически попадает в errorBoundary, позволяя корректно отобразить состояние ошибки, не ломая приложение.


Переходы с зависимостями

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

const userRoute = router.createRoute({
  path: '/users/:userId',
  children: [
    {
      path: 'settings',
      component: UserSettingsPage,
    },
    {
      path: 'posts',
      component: UserPostsPage,
    },
  ],
})

При переходе из /users/1 в /users/1/posts TanStack Router автоматически определяет общий родительский маршрут, выполняет onExit и onEnter только для затронутых маршрутов, минимизируя лишние перерендеры.


Пошаговое управление переходом

TanStack Router поддерживает интерактивное управление переходом, что особенно полезно для сложных приложений:

  1. Запуск перехода через router.navigate().
  2. Выполнение асинхронных loader и проверок.
  3. Состояние transition.state = 'loading'.
  4. Вызов onExit старого маршрута и onEnter нового.
  5. Обновление текущего маршрута после успешного завершения всех шагов.

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


Вывод

Механизм переходов в TanStack Router сочетает гибкость и контроль. Он объединяет асинхронные загрузки данных, хуки для входа и выхода с маршрутов, блокировку переходов, обработку ошибок и возможность интеграции анимаций. Такой подход делает маршрутизацию мощным инструментом для построения сложных веб-приложений, где точное управление навигацией критично для пользовательского опыта.