Композиция маршрутов

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

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

В React Router (версии 6 и выше) вложенные маршруты задаются через Routes и Route, где один маршрут может содержать другие маршруты в качестве дочерних. Это позволяет создавать многоуровневые структуры, которые отражают архитектуру приложения.

Пример вложенного маршрута:

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Dashboard from './Dashboard';
import Profile from './Profile';
import Settings from './Settings';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/dashboard" element={<Dashboard />}>
          <Route path="profile" element={<Profile />} />
          <Route path="settings" element={<Settings />} />
        </Route>
      </Routes>
    </Router>
  );
}

В этом примере Profile и Settings являются подмаршрутами Dashboard. Для отображения вложенного содержимого внутри компонента Dashboard используется <Outlet />:

import { Outlet } from 'react-router-dom';

function Dashboard() {
  return (
    <div>
      <h1>Панель управления</h1>
      <Outlet />
    </div>
  );
}

<Outlet /> рендерит дочерние маршруты, позволяя создать шаблон страницы с общей структурой.

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

Динамические сегменты маршрута позволяют передавать данные через URL. Для этого в path используется синтаксис с двоеточием (:):

<Route path="/user/:userId" element={<UserProfile />} />

Внутри компонента можно получить значение параметра через хук useParams:

import { useParams } from 'react-router-dom';

function UserProfile() {
  const { userId } = useParams();
  return <div>Профиль пользователя: {userId}</div>;
}

Динамические параметры удобно комбинировать с вложенными маршрутами для создания страниц профиля с вкладками:

<Route path="/user/:userId" element={<UserProfile />}>
  <Route path="posts" element={<UserPosts />} />
  <Route path="settings" element={<UserSettings />} />
</Route>

Индексные маршруты

Индексные маршруты используются для отображения содержимого по умолчанию при переходе на родительский маршрут. Они объявляются с помощью атрибута index:

<Route path="/dashboard" element={<Dashboard />}>
  <Route index element={<DashboardHome />} />
  <Route path="profile" element={<Profile />} />
</Route>

При переходе на /dashboard будет рендериться DashboardHome, а при /dashboard/profile — компонент Profile.

Ленивая загрузка маршрутов

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

import { Suspense, lazy } from 'react';
import { Routes, Route } from 'react-router-dom';

const Dashboard = lazy(() => import('./Dashboard'));
const Profile = lazy(() => import('./Profile'));

function App() {
  return (
    <Suspense fallback={<div>Загрузка...</div>}>
      <Routes>
        <Route path="/dashboard" element={<Dashboard />}>
          <Route path="profile" element={<Profile />} />
        </Route>
      </Routes>
    </Suspense>
  );
}

Защищённые маршруты

Композиция маршрутов позволяет легко реализовать защищённые маршруты через обёртки-компоненты. Например, компонент PrivateRoute проверяет авторизацию перед рендером дочерних маршрутов:

import { Navigate, Outlet } from 'react-router-dom';

function PrivateRoute({ isAuthenticated }) {
  return isAuthenticated ? <Outlet /> : <Navigate to="/login" />;
}

// Использование
<Route element={<PrivateRoute isAuthenticated={true} />}>
  <Route path="/dashboard" element={<Dashboard />} />
  <Route path="/dashboard/profile" element={<Profile />} />
</Route>

Условная компоновка маршрутов

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

const routes = [
  { path: '/home', element: <Home /> },
  { path: '/about', element: <About /> },
];

function App() {
  return (
    <Routes>
      {routes.map((route, index) => (
        <Route key={index} path={route.path} element={route.element} />
      ))}
    </Routes>
  );
}

Такой подход упрощает управление маршрутизацией при масштабировании приложения.

Навигация и ссылки между маршрутами

Для перехода между маршрутами используются компоненты Link и NavLink. NavLink позволяет добавить активное состояние для текущего маршрута:

import { NavLink } from 'react-router-dom';

function Navigation() {
  return (
    <nav>
      <NavLink to="/home" className={({ isActive }) => isActive ? 'active' : ''}>Главная</NavLink>
      <NavLink to="/about" className={({ isActive }) => isActive ? 'active' : ''}>О проекте</NavLink>
    </nav>
  );
}

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

Обработка ошибок и несуществующих маршрутов

Для обработки ошибок навигации используется маршрут с path="*":

<Route path="*" element={<NotFound />} />

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

Вывод

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