Router API

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

Ключевые элементы Router API:

  • Route — базовый строительный блок маршрутизации. Каждый маршрут описывает путь (path), компонент (component) и опциональные свойства, такие как loader, action или children.
  • Router — объект, который объединяет все маршруты в приложение и управляет навигацией, состоянием маршрутов и историей.
  • Loader — асинхронная функция для предварительной загрузки данных перед рендером компонента.
  • Action — функция для обработки мутаций данных, связанных с конкретным маршрутом.
  • Outlet — компонент, который рендерит дочерние маршруты текущего маршрута.

Создание маршрутов

Маршруты создаются с использованием функции createRoute или декларативно через объектную структуру. Основная схема маршрута выглядит так:

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

const homeRoute = createRoute({
  path: '/',
  component: HomePage,
  loader: async () => {
    return fetch('/api/home').then(res => res.json());
  },
});

Особенности определения маршрута:

  • path поддерживает динамические сегменты, например /users/:userId.
  • component может быть любым React-компонентом.
  • loader возвращает данные, доступные через хук useLoaderData.
  • children позволяют строить вложенные маршруты, создавая иерархию.

Динамические параметры маршрутов

Для маршрутов с переменными сегментами используются двоеточия в path. Доступ к параметрам осуществляется через хуки.

const userRoute = createRoute({
  path: '/users/:userId',
  component: UserPage,
  loader: async ({ params }) => {
    return fetch(`/api/users/${params.userId}`).then(res => res.json());
  },
});
  • params содержит значения всех динамических сегментов.
  • Можно использовать TypeScript для типизации параметров маршрута, обеспечивая строгую проверку.

Вложенные маршруты и Outlet

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

const dashboardRoute = createRoute({
  path: '/dashboard',
  component: DashboardLayout,
  children: [
    createRoute({ path: 'stats', component: StatsPage }),
    createRoute({ path: 'settings', component: SettingsPage }),
  ],
});
  • DashboardLayout содержит <Outlet />, через который отображаются StatsPage или SettingsPage.
  • Вложенность маршрутов позволяет разделять логику загрузки данных и интерфейса на уровни.

Навигация и ссылки

Для перехода между маршрутами используется функция navigate и компонент Link.

import { useRouter, Link } from '@tanstack/router';

const Navigation = () => {
  const router = useRouter();
  
  return (
    <>
      <Link to="/">Главная</Link>
      <button onCl ick={() => router.navigate('/dashboard/stats')}>
        Перейти к статистике
      </button>
    </>
  );
};
  • navigate(path, options) поддерживает replace, state и другие параметры.
  • Link автоматически предотвращает перезагрузку страницы и поддерживает активные состояния.

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

TanStack Router обеспечивает встроенную поддержку асинхронных данных через loader.

const postsRoute = createRoute({
  path: '/posts',
  component: PostsPage,
  loader: async () => {
    const response = await fetch('/api/posts');
    if (!response.ok) throw new Error('Ошибка загрузки постов');
    return response.json();
  },
});
  • Ошибки в loader можно обработать через отдельные маршруты ошибок.
  • useLoaderData возвращает результат загрузки внутри компонента.

Действия (Actions) для форм и мутаций

Action используется для обработки POST-запросов или других мутаций данных на маршруте.

const createPostRoute = createRoute({
  path: '/posts/new',
  component: NewPostPage,
  action: async ({ formData }) => {
    const response = await fetch('/api/posts', {
      method: 'POST',
      body: JSON.stringify(Object.fromEntries(formData)),
    });
    return response.json();
  },
});
  • Действия интегрируются с формами через <form method="post">.
  • Результат action доступен через хук useActionData.

Работа с состоянием маршрутов

Router хранит текущее состояние навигации, активные маршруты и результаты загрузки данных. Это позволяет реализовать:

  • Предзагрузку данных при переходе.
  • Кэширование результатов loader.
  • Управление переходами с анимациями или блокировкой, если данные не загружены.

Роутинг с вложенными параметрами и относительными ссылками

Поддержка вложенных параметров и относительных переходов упрощает навигацию в больших приложениях:

<Link to="stats">Статистика</Link> 
<Link to="../settings">Настройки</Link>
  • to поддерживает абсолютные и относительные пути.
  • Относительные пути упрощают организацию вложенных маршрутов без дублирования базового сегмента.

Поддержка ошибок и загрузки

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

const errorRoute = createRoute({
  path: '*',
  component: ErrorPage,
});
  • errorElement можно назначить на конкретный маршрут для обработки ошибок loader или action.
  • pendingElement позволяет показывать индикаторы загрузки при асинхронных переходах.

Интеграция с TypeScript

TanStack Router тесно интегрирован с TypeScript:

  • Параметры маршрутов типизируются автоматически.
  • Результаты loader и action можно строго типизировать.
  • Автодополнение и проверки типов сокращают количество ошибок на этапе разработки.
type UserParams = { userId: string };

const userRoute = createRoute<UserParams>({
  path: '/users/:userId',
  loader: async ({ params }) => {
    // params.userId строго типизирован
  },
});

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