TanStack Router и TanStack Query

TanStack Router — это современная библиотека маршрутизации для JavaScript, ориентированная на работу с React и другими фронтенд-фреймворками. Основной концепцией является декларативное определение маршрутов и возможность интеграции с асинхронными данными через TanStack Query.

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

import { createRouter, Route } from '@tanstack/router';

const router = createRouter({
  routeTree: Route({
    path: '/',
    component: HomePage,
    children: [
      Route({
        path: 'about',
        component: AboutPage,
      }),
      Route({
        path: 'users/:userId',
        component: UserPage,
      }),
    ],
  }),
});

Ключевым элементом является иерархическая структура маршрутов, что позволяет строить вложенные маршруты, поддерживающие динамические параметры. Параметры маршрутов доступны через useParams():

import { useParams } from '@tanstack/router';

function UserPage() {
  const { userId } = useParams();
  return 
ID пользователя: {userId}
; }

Асинхронные данные и TanStack Query

TanStack Router тесно интегрируется с TanStack Query для загрузки данных при навигации. Основной подход — использование loaders на маршрутах. Loader возвращает промис с данными, которые будут доступны в компоненте через хук useLoaderData():

import { useLoaderData } from '@tanstack/router';
import { queryClient } from './queryClient';
import { fetchUser } from './api';

const userRoute = Route({
  path: 'users/:userId',
  loader: async ({ params }) => {
    return queryClient.fetchQuery(['user', params.userId], () =>
      fetchUser(params.userId)
    );
  },
  component: UserPage,
});

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

Интеграция с React Query

TanStack Router использует TanStack Query для оптимизации сетевых запросов. Это позволяет:

  • Кэшировать данные между переходами.
  • Декларировать зависимые запросы.
  • Поддерживать инвалидацию и повторную загрузку при изменении параметров маршрута.

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

const postsRoute = Route({
  path: 'users/:userId/posts',
  loader: async ({ params }) => {
    const user = await queryClient.fetchQuery(['user', params.userId], () =>
      fetchUser(params.userId)
    );
    return queryClient.fetchQuery(['posts', user.id], () => fetchPosts(user.id));
  },
  component: UserPostsPage,
});

Такой подход позволяет выстраивать цепочки асинхронных данных без дублирования кода и обеспечивает высокую производительность при навигации.

Навигация и управление состоянием

TanStack Router предоставляет несколько способов навигации:

  • Программная навигация через router.navigate({ to: '/path' }).
  • Ссылки через компонент Link:
import { Link } from '@tanstack/router';

О нас

Каждый маршрут может содержать динамические параметры, которые автоматически подставляются в URL:

Профиль пользователя 123

Маршруты поддерживают стратегии загрузки данных: eager (загрузка сразу при рендере) и lazy (по необходимости), что позволяет оптимизировать производительность приложений.

Обработка ошибок и загрузки

Встроенные механизмы маршрутизатора обеспечивают удобную обработку состояния:

  • ErrorBoundary для перехвата ошибок загрузки данных или рендеринга компонентов.
  • PendingBoundary для отображения индикаторов загрузки во время асинхронных операций.

Пример с отображением состояния загрузки:

Оптимизация и кэширование

TanStack Router совместно с TanStack Query позволяет кэшировать данные на уровне маршрутов. Можно использовать query keys, чтобы управлять временем жизни кэша и инвалидацией данных при навигации:

queryClient.invalidateQueries(['user', userId]);

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

Поддержка вложенных и динамических маршрутов

Вложенные маршруты реализуются через иерархическую структуру, где каждый дочерний маршрут наследует контекст родителя:

const router = createRouter({
  routeTree: Route({
    path: '/',
    component: Layout,
    children: [
      Route({
        path: 'dashboard',
        component: Dashboard,
        children: [
          Route({ path: 'stats', component: StatsPage }),
          Route({ path: 'settings', component: SettingsPage }),
        ],
      }),
    ],
  }),
});

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