Лучшие практики работы с контекстом

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


Создание контекста

Контекст в TanStack Router создаётся через функцию createContext, которая возвращает объект с методами useContext и Provider. Пример создания:

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

const AuthContext = createContext({
  token: null,
  setToken: () => {},
});

В данном примере создаётся контекст авторизации с токеном и функцией для его изменения.

Ключевые моменты:

  • Контексты должны содержать минимальный набор данных, необходимый для всех компонентов маршрута, чтобы избежать лишних рендеров.
  • Функции для изменения состояния (например, setToken) рекомендуется инкапсулировать внутри контекста для сохранения единообразия и предсказуемости.

Подключение контекста к маршрутам

Контекст предоставляется маршрутам через компонент Provider:

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

const router = createRouter({
  routeTree: createRoute({
    path: '/',
    component: RootComponent,
    context: AuthContext.Provider,
  }),
});

Внутри RootComponent все дочерние маршруты могут использовать контекст через useContext:

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

function Header() {
  const { token, setToken } = useContext(AuthContext);

  return (
    
{token ? 'Вы вошли в систему' : 'Гость'}
); }

Рекомендации:

  • Контекст следует подключать как можно выше в дереве маршрутов, если он нужен нескольким дочерним маршрутам.
  • Избегать многократного создания Provider на одном и том же уровне, чтобы не дублировать состояние.

Динамические контексты и загрузка данных

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

const UserContext = createContext({ user: null });

const userRoute = createRoute({
  path: '/user',
  loader: async () => {
    const response = await fetch('/api/user');
    return response.json();
  },
  context: ({ loaderData }) => ({
    user: loaderData,
  }),
});
  • Функция context получает loaderData маршрута, что позволяет сразу передавать загруженные данные в контекст.
  • Это повышает производительность, так как дочерние компоненты не делают повторных запросов и получают данные напрямую из контекста.

Состояние контекста и реактивность

Контекст TanStack Router тесно интегрирован с реактивностью компонентов. Любое изменение состояния внутри контекста автоматически вызывает перерендеринг дочерних компонентов, использующих useContext.

Лучшие практики управления состоянием:

  • Разделять состояние на мелкие контексты, если части интерфейса не зависят друг от друга, чтобы снизить количество лишних рендеров.
  • Использовать useMemo и useCallback для функций в контексте, чтобы предотвратить пересоздание функций на каждом рендере.
  • Для сложных состояний можно комбинировать контекст с useReducer, что упрощает управление сложными изменениями данных.

Комбинирование нескольких контекстов

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


  
    
  

Рекомендации:

  • Стараться минимизировать глубину вложенности контекстов, иначе увеличивается сложность отслеживания состояния.
  • Если контексты связаны, объединять их в один объект состояния с разделением логики внутри.

Практические советы

  • Для глобальных данных использовать контексты на уровне корневого маршрута.
  • Для данных, относящихся только к конкретному маршруту, создавать локальный контекст именно для него.
  • Всегда документировать структуру контекстов и их назначение, чтобы новые разработчики могли быстро ориентироваться.
  • Избегать передачи больших объектов в контексте, которые часто меняются — это может вызвать лишние рендеры компонентов.
  • Использовать loaderData совместно с контекстом для уменьшения количества запросов и централизованного хранения данных.