Параллельные маршруты

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

Основные принципы параллельных маршрутов

Параллельные маршруты строятся на основе root route, который может содержать несколько дочерних маршрутов, каждый из которых рендерится в своем outlet. Каждый параллельный маршрут управляет своей областью состояния и отображения, не влияя на соседние маршруты. Это позволяет одновременно изменять несколько участков интерфейса без перезагрузки всего приложения.

Ключевые концепции:

  • Outlet — контейнер для дочерних маршрутов. Параллельные маршруты могут иметь отдельные outlet’ы.
  • Route IDs — уникальные идентификаторы маршрутов, которые используются для точного управления навигацией.
  • Parent-child hierarchy — маршруты могут наследовать параметры и загрузчики данных от родителя, но при этом работать независимо в пределах параллельной ветки.

Определение параллельных маршрутов

Для создания параллельных маршрутов используется структура route tree, где каждый дочерний маршрут может быть включен как parallel route branch. В TanStack Router это делается через children и outletId. Пример структуры:

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

const routeConfig = createRouteConfig()
  .createRoute({
    path: '/',
    component: RootComponent,
    children: [
      {
        path: 'main',
        component: MainComponent,
        children: [
          {
            path: 'sidebar',
            component: SidebarComponent,
            outletId: 'sidebarOutlet',
          },
          {
            path: 'content',
            component: ContentComponent,
            outletId: 'contentOutlet',
          },
        ],
      },
    ],
  });

const router = createRouter({ routeConfig });

В этом примере sidebar и content являются параллельными маршрутами. Их рендеринг происходит в разных outlet, которые нужно определить в RootComponent и MainComponent:

function MainComponent() {
  return (
    <div>
      <div id="sidebarOutlet" />
      <div id="contentOutlet" />
    </div>
  );
}

Навигация между параллельными маршрутами

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

router.navigate({
  to: '/main/sidebar',
  outletId: 'sidebarOutlet'
});

При этом компонент в contentOutlet остается на месте, и только SidebarComponent будет обновлен.

Асинхронная загрузка данных в параллельных маршрутах

Каждый параллельный маршрут может иметь свои loaders и actions для загрузки данных. Важно, что загрузка данных одной ветки не блокирует другие:

const sidebarRoute = routeConfig.createRoute({
  path: 'sidebar',
  loader: async () => {
    const response = await fetch('/api/sidebar');
    return response.json();
  },
  component: SidebarComponent,
  outletId: 'sidebarOutlet',
});

Здесь loader выполняется только для sidebarRoute, что позволяет экономить ресурсы и ускорять отклик интерфейса.

Обработка ошибок в параллельных маршрутах

TanStack Router поддерживает отдельные error boundaries для каждой ветки. Если в одной параллельной ветке возникает ошибка, остальные ветки продолжают работать:

const contentRoute = routeConfig.createRoute({
  path: 'content',
  component: ContentComponent,
  errorComponent: ContentErrorComponent,
  outletId: 'contentOutlet',
});

При ошибке загрузки данных или рендеринга в contentRoute, пользователь увидит только ContentErrorComponent, а sidebarOutlet останется полностью функциональным.

Практические сценарии использования

  • Дашборды и панели управления: разные виджеты обновляются независимо, каждый в своем параллельном маршруте.
  • Редакторы с боковой панелью: изменения в боковой панели не влияют на основной контент.
  • Приложения с динамическими вкладками: каждая вкладка может быть отдельной параллельной веткой.

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