Хотите заказать веб-сайт? Связаться с нами

Маршрутизация в React — навигация по компонентам

Цель урока: научиться создавать многостраничные React-приложения с помощью React Router. Понять, как работают маршруты, навигация, вложенные маршруты и защищённые страницы. Узнать, почему @reach/router больше не используется и что выбрать для новых проектов.

Маршрутизация в React — навигация по компонентам

Введение: зачем нужен роутер в React

React-приложения по умолчанию — это Single Page Application (SPA). Весь интерфейс живёт на одной странице, а смена "страниц" происходит без перезагрузки браузера.

Проблема:

Как сделать так, чтобы при переходе по URL /about показывалась одна страница, а при /profile — другая?

Главная мысль: роутер синхронизирует URL и интерфейс. Когда пользователь переходит по ссылке или вводит адрес вручную, роутер определяет, какой компонент показать.

Две основные библиотеки:

  1. React Router — стандарт де-факто для React, активно развивается
  2. @reach/router — устаревшая библиотека, не рекомендуется для новых проектов

Почему @reach/router больше не используется

Важно: @reach/router официально устарел (deprecated). Вместо него используйте React Router v6+.

Причины:

  1. Разработка остановлена
  2. Авторы объединили лучшие идеи из обеих библиотек в React Router v6
  3. React Router v6 стал меньше по размеру и мощнее по возможностям

Что произошло:

React Router и @reach/router объединились. Если бы существовал @reach/router v2, он выглядел бы как React Router v6. Поэтому в новых проектах всегда выбирайте React Router.

React Router v6 — установка и базовый синтаксис

Установка:

npm install react-router-dom@6

Базовый пример:

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

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<HomePage />} />
        <Route path="/about" element={<AboutPage />} />
        <Route path="/contact" element={<ContactPage />} />
        <Route path="*" element={<NotFoundPage />} />
      </Routes>
    </BrowserRouter>
  );
}

Что здесь происходит:

  1. BrowserRouter — обёртка, дающая доступ к URL
  2. Routes — контейнер для маршрутов (заменил Switch)
  3. Route — определяет соответствие пути и компонента
  4. path="*" — «ловит» все несуществующие пути (404)

Навигация с помощью Link и NavLink

Link — для обычных переходов:

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

function Navigation() {
  return (
    <nav>
      <Link to="/">Главная</Link>
      <Link to="/about">О нас</Link>
      <Link to="/contact">Контакты</Link>
    </nav>
  );
}

NavLink — для активных ссылок:

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

function Navigation() {
  return (
    <nav>
      <NavLink 
        to="/" 
        className={({ isActive }) => isActive ? 'active' : ''}
      >
        Главная
      </NavLink>
      <NavLink 
        to="/about"
        style={({ isActive }) => ({
          color: isActive ? 'red' : 'black'
        })}
      >
        О нас
      </NavLink>
    </nav>
  );
}

Программная навигация (через код):

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

function LoginButton() {
  const navigate = useNavigate();

  const handleLogin = () => {
    // После успешного входа перенаправляем
    navigate('/dashboard');
  };

  return <button onClick={handleLogin}>Войти</button>;
}
Из ноутбука появляются летающие светящиеся экраны

Оживи свой сайт. Освой JavaScript!

Статичная верстка — это только скелет. Наш онлайн-курс "JavaScript с нуля до профи" даст твоим страницам мышцы и нервы. Научись создавать слайдеры, формы, интерактивные карты и получать данные с сервера.

От теории — к реальным скриптам в твоём портфолио.

Подробнее о курсе

Вложенные маршруты (Nested Routes)

Одна из главных фишек React Router — возможность вкладывать маршруты друг в друга.

Структура:

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Layout />}>
          <Route index element={<HomePage />} />
          <Route path="about" element={<AboutPage />} />
          <Route path="dashboard" element={<DashboardLayout />}>
            <Route index element={<DashboardHome />} />
            <Route path="settings" element={<Settings />} />
            <Route path="profile" element={<Profile />} />
          </Route>
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

Компонент Layout с Outlet:

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

function Layout() {
  return (
    <div>
      <header>
        <nav>
          <Link to="/">Главная</Link>
          <Link to="/about">О нас</Link>
          <Link to="/dashboard">Панель</Link>
        </nav>
      </header>
      
      <main>
        <Outlet /> {/* Здесь рендерятся вложенные маршруты */}
      </main>
      
      <footer>Подвал</footer>
    </div>
  );
}

Что дают вложенные маршруты:

  1. Общий макет (хедер, сайдбар, футер) для группы страниц
  2. Изоляция логики на разных уровнях
  3. Чистая структура кода

Параметры маршрута (Route Parameters)

Динамические сегменты URL:

// Определение маршрута с параметром
<Route path="/users/:userId" element={<UserProfile />} />

Получение параметров в компоненте:

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

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

Несколько параметров:

<Route path="/products/:category/:productId" element={<Product />} />

function Product() {
  const { category, productId } = useParams();
  return <p>Категория: {category}, Товар: {productId}</p>;
}

Query-параметры (строка запроса)

Доступ к query-параметрам:

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

function SearchPage() {
  const location = useLocation();
  const searchParams = new URLSearchParams(location.search);
  
  const query = searchParams.get('q');
  const page = searchParams.get('page') || '1';
  
  return <p>Поиск: {query}, Страница: {page}</p>;
}
// /search?q=react&page=2 → query = "react", page = "2"

Защищённые маршруты (Private Routes)

Компонент-обёртка для проверки авторизации:

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

function ProtectedRoute({ isAuthenticated }) {
  if (!isAuthenticated) {
    return <Navigate to="/login" replace />;
  }
  
  return <Outlet />;
}

// Использование
function App() {
  const [isAuth, setIsAuth] = useState(false);

  return (
    <BrowserRouter>
      <Routes>
        <Route path="/login" element={<Login />} />
        
        <Route element={<ProtectedRoute isAuthenticated={isAuth} />}>
          <Route path="/dashboard" element={<Dashboard />} />
          <Route path="/profile" element={<Profile />} />
          <Route path="/settings" element={<Settings />} />
        </Route>
        
        <Route path="*" element={<NotFound />} />
      </Routes>
    </BrowserRouter>
  );
}

Lazy Loading (ленивая загрузка маршрутов)

Оптимизация производительности — загрузка кода страницы только когда она нужна:

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

const HomePage = lazy(() => import('./pages/HomePage'));
const AboutPage = lazy(() => import('./pages/AboutPage'));
const DashboardPage = lazy(() => import('./pages/DashboardPage'));

function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<div>Загрузка...</div>}>
        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="/about" element={<AboutPage />} />
          <Route path="/dashboard" element={<DashboardPage />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

React Router v6.4+ — Data Router (расширенные возможности)

Начиная с версии 6.4, появился Data Router с поддержкой:

Возможность Что даёт
Loaders Загрузка данных до рендера компонента
Actions Обработка отправки форм
Error Boundaries Отлов ошибок на уровне маршрутов

Пример с loader:

import { createBrowserRouter, RouterProvider } from 'react-router-dom';

const router = createBrowserRouter([
  {
    path: '/users/:id',
    element: <UserDetail />,
    loader: async ({ params }) => {
      const response = await fetch(`/api/users/${params.id}`);
      if (!response.ok) throw new Error('Пользователь не найден');
      return response.json();
    },
    errorElement: <ErrorPage />
  }
]);

function App() {
  return <RouterProvider router={router} />;
}

Почему это лучше useEffect?

  1. Данные загружаются до рендера, нет «моргания» загрузки
  2. Поддержка параллельной загрузки данных
  3. Встроенная обработка ошибок

Сравнительная таблица: React Router vs @reach/router

Характеристика React Router v6 @reach/router
Статус Активно развивается Устарел (deprecated)
Кому принадлежит React Training + Remix Команда React (передана)
Размер ~3kb (оптимизирован) Больше
Вложенные маршруты ✅ Отличная поддержка ✅ Были основой
Хуки useParams, useNavigate, useLocation Есть, но устарели
Data Router ✅ Да (v6.4+) ❌ Нет
Перспектива Единственный выбор для новых проектов Не рекомендуется

Частые ошибки новичков

Ошибка Почему Исправление
Забывают обернуть в BrowserRouter Routes не работают Добавь <BrowserRouter> на верхний уровень
Используют Switch вместо Routes Синтаксис старой версии Обнови до Routes
Не закрывают тег Route Ошибка синтаксиса Правильно: <Route path="/" element={<Page />} />
Забывают * для 404 Несуществующие пути не обрабатываются Добавь <Route path="*" element={<NotFound />} />
Пытаются использовать @reach/router Библиотека устарела Используй react-router-dom v6+

Итог (что мы узнали)

Что узнали Как это работает
React Router vs @reach/router @reach/router устарел, React Router — стандарт
BrowserRouter Обёртка для маршрутизации
Routes и Route Контейнеры для путей и компонентов
Link и NavLink Навигация без перезагрузки
useNavigate Программная навигация
Вложенные маршруты Outlet для вложенных компонентов
useParams Доступ к динамическим параметрам
useLocation Доступ к query-параметрам
Защищённые маршруты Проверка авторизации через Navigate
Data Router Загрузка данных до рендера (v6.4+)

Задание для закрепления (на 1–2 часа)

Создай полноценное многостраничное приложение:

  1. Главная страница (/) — приветствие и ссылки на другие страницы
  2. О нас (/about) — информация о проекте
  3. Профиль пользователя (/users/:id) — показывает имя и email пользователя (используй useParams)
  4. Поиск (/search?q=...) — показывает поисковый запрос из URL
  5. 404 — для всех несуществующих путей
  6. Защищённый раздел (/admin) — доступен только при isAuthenticated === true (используй ProtectedRoute)

Дополнительно:

  1. Используй NavLink с активным стилем для меню
  2. Добавь ленивую загрузку для страниц
  3. (Опционально) Используй createBrowserRouter с loader для загрузки данных пользователя по ID

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

Пример выполненного задания

React Router

Скачать код выполненного задания: (откроется в новой вкладке)React Router.

Что дальше?

В следующем уроке разберём SSR (Server Side Rendering) с Next.js — как создавать React-приложения, которые рендерятся на сервере. Ты узнаешь, как улучшить SEO, ускорить загрузку и создавать полноценные веб-приложения с Next.js.

Теги: