Типизация контекста

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


Определение контекста маршрута

Контекст в TanStack Router создается с помощью generic-параметров маршрута. Каждый маршрут может иметь собственный тип контекста, который затем доступен в обработчиках и компонентах маршрута.

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

type UserContext = {
  userId: string;
  isAdmin: boolean;
};

const userRoute = createRoute({
  path: '/user/:userId',
  context: {} as UserContext,
});

В данном примере UserContext описывает данные, которые будут доступны маршруту /user/:userId. Объявление через context: {} as UserContext гарантирует, что любые обращения к контексту маршрута будут строго соответствовать типу UserContext.


Использование контекста в обработчиках

Контекст становится доступным в функциях загрузки данных (loader) и в компонентах, привязанных к маршруту. TanStack Router обеспечивает строгую типизацию этих функций через generic-параметры маршрута.

const userRoute = createRoute({
  path: '/user/:userId',
  context: {} as UserContext,
  loader: async ({ context }) => {
    // context.userId и context.isAdmin доступны и строго типизированы
    const response = await fetch(`/api/users/${context.userId}`);
    return response.json();
  },
});

Здесь context автоматически имеет тип UserContext. Ошибки обращения к несуществующим полям будут выявлены TypeScript на этапе компиляции, что предотвращает потенциальные runtime-ошибки.


Наследование и объединение контекста

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

type AppContext = {
  sessionId: string;
};

type DashboardContext = AppContext & {
  dashboardId: string;
};

const appRoute = createRoute({
  path: '/',
  context: {} as AppContext,
});

const dashboardRoute = createRoute({
  path: '/dashboard/:dashboardId',
  parent: appRoute,
  context: {} as DashboardContext,
});

В этом примере дочерний маршрут dashboardRoute наследует поля родительского контекста и добавляет собственное поле dashboardId. Любая попытка использовать контекст без учёта этих полей будет отмечена TypeScript как ошибка.


Типизация параметров маршрута вместе с контекстом

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

type UserParams = { userId: string };
type UserContext = { isAdmin: boolean };

const userRoute = createRoute({
  path: '/user/:userId',
  params: {} as UserParams,
  context: {} as UserContext,
});

const loader = async ({ params, context }: { params: UserParams; context: UserContext }) => {
  if (context.isAdmin) {
    return fetch(`/api/admin/users/${params.userId}`).then(res => res.json());
  }
  return fetch(`/api/users/${params.userId}`).then(res => res.json());
};

Параметры params и контекст context строго типизированы, что позволяет безопасно использовать их внутри асинхронных загрузчиков данных.


Интеграция с React компонентами

При использовании TanStack Router с React контекст маршрута автоматически становится доступным через хук useRouteContext. Типизация контекста обеспечивает автодополнение и проверку типов внутри компонентов.

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

function UserProfile() {
  const { userId, isAdmin } = useRouteContext<UserContext>();
  
  return (
    <div>
      <p>User ID: {userId}</p>
      <p>Admin Access: {isAdmin ? 'Yes' : 'No'}</p>
    </div>
  );
}

Компилятор TypeScript проверяет, что userId и isAdmin существуют в контексте и имеют правильные типы. Попытка обратиться к несуществующему полю приведет к ошибке на этапе разработки.


Рекомендации по организации контекста

  • Создавать отдельные типы для каждого маршрута. Это повышает читаемость и делает контекст маршрута независимым.
  • Использовать объединение типов для наследуемых маршрутов, чтобы избегать дублирования.
  • Строго типизировать loaders и actions с учетом контекста, чтобы гарантировать корректность работы всех функций маршрута.
  • Использовать хук useRouteContext только с правильно типизированными контекстами, чтобы избежать ошибок при извлечении данных внутри компонентов.

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