Уменьшение ререндеров

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

Разделение маршрутов и вложенные маршруты

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

const rootRoute = createRoute({
  path: "/",
  component: RootComponent,
  children: [
    {
      path: "dashboard",
      component: Dashboard,
    },
    {
      path: "settings",
      component: Settings,
    },
  ],
});

В этом примере RootComponent не будет перерендериваться при переходе между dashboard и settings, так как оба маршрута находятся на одном уровне вложенности и не изменяют состояние родителя.

Использование loader и кеширование данных

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

const dashboardRoute = createRoute({
  path: "dashboard",
  component: Dashboard,
  loader: async () => {
    const response = await fetch("/api/dashboard");
    return response.json();
  },
});

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

Оптимизация компонент через React.memo и useMemo

Даже при правильной настройке маршрутов компоненты могут ререндериться из-за изменений пропсов. Использование React.memo и useMemo помогает предотвращать лишние перерендеры дочерних компонентов:

const DashboardStats = React.memo(({ stats }) => {
  return <StatsTable data={stats} />;
});

Для вычисляемых данных, зависящих от пропсов, useMemo позволяет мемоизировать результат:

const filteredItems = useMemo(() => items.filter(item => item.active), [items]);

Контроль над обновлениями через useMatch и useNavigate

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

const match = useMatch("/dashboard");
if (match) {
  // Компонент реагирует только на конкретный путь
}

Это обеспечивает локальное управление ререндером: компонент обновляется только если совпадает путь, а не при каждом изменении маршрута глобально.

Отделение глобального состояния от маршрутов

Частая причина лишних ререндеров — смешение глобального состояния с состоянием маршрутов. Рекомендуется использовать отдельные менеджеры состояния (например, Zustand, Redux Toolkit) для глобальных данных, а TanStack Router — только для управления навигацией и загрузкой данных конкретных маршрутов.

const userStore = create((set) => ({
  user: null,
  setUser: (user) => set({ user }),
}));

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

Lazy loading и код-сплиттинг

TanStack Router поддерживает ленивую загрузку компонентов через React.lazy и Suspense. Это позволяет рендерить только необходимые компоненты для текущего маршрута:

const Dashboard = React.lazy(() => import("./Dashboard"));

const dashboardRoute = createRoute({
  path: "dashboard",
  component: () => (
    <Suspense fallback={<div>Загрузка...</div>}>
      <Dashboard />
    </Suspense>
  ),
});

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

Минимизация перерендеров при навигации

Навигация по маршрутам TanStack Router может быть оптимизирована через свойства preserveScroll, preventScrollReset и defaultPending:

const settingsRoute = createRoute({
  path: "settings",
  component: Settings,
  defaultPending: true,
});

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

Итоговые рекомендации по уменьшению ререндеров

  • Делать грамотное вложение маршрутов, чтобы родительские компоненты не рендерились при смене дочерних маршрутов.
  • Использовать loader с кешированием данных для асинхронной загрузки.
  • Применять React.memo и useMemo для оптимизации дочерних компонентов.
  • Контролировать локальные обновления с помощью useMatch и useNavigate.
  • Разделять глобальное состояние и состояние маршрутов.
  • Внедрять lazy loading и код-сплиттинг для тяжелых компонентов.
  • Настраивать параметры маршрутов для минимизации повторных ререндеров при навигации.

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