Урок 22: Введение в маршрутизацию с React Router

Сегодня нас ждет один из самых интересных уроков во всем курсе. Мы будем говорить о маршрутизации.

До этого момента все наши приложения были сосредоточены на одном экране, на одной «странице». Мы управляли состоянием, меняли интерфейс, но не переходили между разными представлениями, как в классических веб-сайтах. Но что, если мы хотим создать многостраничное приложение, например, интернет-магазин с главной страницей, каталогом, корзиной и личным кабинетом? Именно здесь на сцену выходит маршрутизация, а точнее бесценная библиотека React Router.

В этом уроке мы с вами разберемся, что такое SPA, установим React Router и я подробно, на примерах покажу, как использовать его ключевые компоненты: BrowserRouterRoutesRoute и Link. Мы создадим наше первое навигационное приложение. Поехали.

Создаем SPA (Single Page Application)

Давайте начнем с фундаментальной концепции. SPA это одностраничное приложение. Звучит как противоречие, не так ли? Мы только что говорили о «многостраничности», а теперь об одной странице. В чем же тут суть?

Представьте, что вы заходите в огромный, современный торговый центр. Вместо того чтобы выходить на улицу и заходить в новую дверь для каждого магазина, вы просто перемещаетесь внутри одного большого здания. Архитектура здания не меняется, стены, крыша, пол. Но содержимое отдельных «помещений» (магазинов) полностью разное. Ваше веб-приложение это и есть такое здание. Файл index.html, который изначально загружается в браузер это его фундамент и несущие стены. Это и есть наша «единственная страница».

Вся дальнейшая работа происходит внутри этой страницы без ее полной перезагрузки. Когда пользователь кликает на ссылку «Каталог», JavaScript (в нашем случае React и React Router) динамически, прямо в браузере, подменяет содержимое текущего «помещения» (например, компонент Home) на содержимое нового (компонент Catalog). При этом браузер не выполняет новый запрос на сервер за полным HTML-документом. Загружаются только необходимые данные (например, через API), а компоненты рендерятся на клиенте.

Каковы преимущества такого подхода? Во-первых, это невероятная плавность и скорость работы. Отсутствие полных перезагрузок страницы делает взаимодействие с приложением похожим на работу с нативным desktop-приложением. Во-вторых, мы можем эффективно разделять обязанности: backend-сервер отвечает исключительно за данные и бизнес-логику (предоставляет API), а frontend (наше React-приложение) за интерфейс и взаимодействие с пользователем.

React сам по себе это библиотека для построения пользовательских интерфейсов и в его ядре нет встроенной функциональности для маршрутизации. Именно поэтому сообщество создало прекрасную библиотеку React Router, которая стала де-факто стандартом для организации навигации в React-приложениях. Она идеально вписывается в философию React, позволяя нам объявлять наши «маршруты» (routes) как компоненты.

Установка React Router

Первым делом нам нужно добавить React Router в наш проект. Если вы создавали проект с помощью create-react-app или Vite, это делается очень просто через менеджер пакетов npm или Yarn.

Откройте терминал в корневой директории вашего проекта и выполните одну из следующих команд:

Через npm:

bash
npm install react-router-dom

Через Yarn:

bash
yarn add react-router-dom

Обратите внимание на название пакета: react-router-dom. Существует также пакет react-router, который содержит логику маршрутизатора. Однако react-router-dom это обертка для веб-приложений, которая включает в себя весь функционал react-router плюс специфические для DOM (браузера) компоненты, такие как <BrowserRouter><Link> и другие. Для создания веб-приложений мы всегда используем react-router-dom.

После завершения установки вы увидите, что пакет добавился в ваши зависимости в файле package.json. Теперь мы можем импортировать его компоненты и начать творить.

Практическая задача 1:
Создайте новый React-проект с помощью вашего любимого инструмента (например, create-react-app или Vite). Установите библиотеку react-router-dom с помощью команды в терминале. Убедитесь, что установка прошла успешно, проверив список зависимостей в package.json.

Компонент BrowserRouter

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

Представьте, что BrowserRouter это умный менеджер, который стоит на входе в наше приложение и говорит: «Я буду следить за адресной строкой. Все компоненты маршрутизации внутри меня будут меняться в зависимости от того, что в ней написано».

Обычно мы оборачиваем им все наше приложение или, по крайней мере, ту его часть, где нужна навигация. Самый распространенный способ, это сделать в файле index.js (или main.jsx), прямо при рендере в ReactDOM.createRoot.

Пример (main.jsx):

jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom'; // 1. Импортируем BrowserRouter
import App from './App';
import './index.css';

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    {/* 2. Оборачиваем все приложение в BrowserRouter */}
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

BrowserRouter использует HTML5 History API (pushStatereplaceStatepopstate), который позволяет управлять историей сессии и URL без перезагрузки страницы. Это то, что дает нам красивые, чистые URL, вроде https://mysite.com/about, вместо старых «якорей» вроде https://mysite.com/#/about.

После того как мы обернули наше приложение в BrowserRouter, мы получаем «зеленый свет» для использования всех остальных компонентов React Router внутри App и его дочерних компонентов.

Компоненты Routes и Route

Сердце нашей маршрутизации, это связка двух компонентов: Routes и Route. Они работают в тандеме, чтобы определить, какой компонент должен быть отображен в зависимости от текущего URL.

<Routes> это контейнер, который хранит в себе все индивидуальные маршруты (<Route>). Его задача просмотреть все свои дочерние <Route> и найти тот, чей путь (path) наилучшим образом соответствует текущему URL. До версии React Router v6 использовался компонент <Switch>, который выбирал первый подходящий маршрут. <Routes> работает умнее, он всегда выбирает наиболее подходящий (самый специфичный) маршрут, что делает код более предсказуемым.

<Route> это индивидуальное правило, карта, которая связывает путь в URL с React-компонентом, который должен быть отображен. У него есть два ключевых пропса:

  • path (строка). Определяет шаблон пути, который должен совпасть с URL (например, /about/users/:id).

  • element (React-элемент). Определяет, какой компонент должен быть отрендерен при совпадении пути. Важный нюанс! В React Router v6 мы передаем не имя компонента, а непосредственно React-элемент, созданный с помощью JSX: element={<About />}, а не component={About}.

Давайте создадим простое приложение с тремя страницами: Главная, О нас и Контакты.

Пример (App.jsx):

jsx
import { Routes, Route } from 'react-router-dom';
import Home from './components/Home';
import About from './components/About';
import Contacts from './components/Contacts';
import './App.css';

function App() {
  return (
    <div className="App">
      {/* Здесь, например, мог бы быть общий для всех страниц хедер */}
      <h1>Добро пожаловать в мое SPA!</h1>

      {/* Контейнер для всех наших маршрутов */}
      <Routes>
        {/* Когда путь точно равен "/", рендерим компонент Home */}
        <Route path="/" element={<Home />} />

        {/* Когда путь равен "/about", рендерим компонент About */}
        <Route path="/about" element={<About />} />

        {/* Когда путь равен "/contacts", рендерим компонент Contacts */}
        <Route path="/contacts" element={<Contacts />} />
      </Routes>

      {/* Здесь, например, мог бы быть общий для всех страниц футер */}
    </div>
  );
}

export default App;

А теперь давайте создадим сами компоненты для страниц. Они будут очень простыми.

components/Home.jsx:

jsx
function Home() {
  return (
    <div>
      <h2>Главная страница</h2>
      <p>Это стартовая страница нашего замечательного SPA.</p>
    </div>
  );
}

export default Home;

components/About.jsx:

jsx
function About() {
  return (
    <div>
      <h2>О нас</h2>
      <p>Мы лучшая компания в мире по производству React-компонентов.</p>
    </div>
  );
}

export default About;

components/Contacts.jsx:

jsx
function Contacts() {
  return (
    <div>
      <h2>Контакты</h2>
      <p>Связаться с нами: contact@example.com</p>
    </div>
  );
}

export default Contacts;

Что теперь происходит? Если вы запустите приложение, то на главной странице (http://localhost:3000/) вы увидите компонент Home. Но как же перейти на /about? Мы пока не создали навигацию. Вы можете попробовать вручную ввести адрес http://localhost:3000/about в адресной строке браузера. Если вы все сделали правильно, вы должны увидеть компонент About! Это и есть суть React Router в действии.

Практическая задача 2:
Создайте базовую структуру приложения, как в примере выше. Убедитесь, что BrowserRouter подключен в main.jsx. Создайте компоненты HomeAbout и Contacts и настройте для них маршруты в App.jsx. Проверьте, что вручную переходя по адресам //about и /contacts в браузере, вы видите соответствующие компоненты.

Компонент Link

Вручную вводить URL в адресной строке, это не самый удобный способ навигации. Пользователи привыкли кликать по ссылкам. Конечно, мы можем использовать обычный HTML-тег <a>:

html
<a href="/about">О нас</a>

Но в чем проблема? При клике на такую ссылку браузер совершит полный переход на новую страницу, то есть отправит запрос на сервер, получит весь index.html заново и перезагрузит все наше приложение с нуля. Это уничтожит все состояние нашего приложения (например, данные в Redux или контексте) и займет больше времени. Это именно то, от чего мы хотим избавиться, создавая SPA.

Вот для этого в React Router есть компонент <Link>. Он рендерит элемент, внешне неотличимый от обычной ссылки, но при клике на него происходит плавный, клиентский переход без перезагрузки страницы. Он использует внутреннюю историю браузера, предоставляемую BrowserRouter.

<Link> принимает проп to, который указывает целевой маршрут. Это аналог атрибута href в теге <a>.

Давайте добавим навигационное меню в наше приложение. Мы можем поместить его, например, прямо в компонент App.

Обновленный App.jsx:

jsx
import { Routes, Route, Link } from 'react-router-dom'; // Импортируем Link
import Home from './components/Home';
import About from './components/About';
import Contacts from './components/Contacts';
import './App.css';

function App() {
  return (
    <div className="App">
      <h1>Добро пожаловать в мое SPA!</h1>

      {/* Навигационное меню */}
      <nav>
        <ul style={{ listStyle: 'none', padding: 0, display: 'flex', gap: '20px' }}>
          <li><Link to="/">Главная</Link></li>
          <li><Link to="/about">О нас</Link></li>
          <li><Link to="/contacts">Контакты</Link></li>
        </ul>
      </nav>

      <hr /> {/* Простая разделительная линия */}

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contacts" element={<Contacts />} />
      </Routes>
    </div>
  );
}

export default App;

Теперь запустите приложение и попробуйте понажимать на ссылки. Вы увидите, как содержимое страницы меняется моментально, без малейшей перезагрузки! При этом URL в адресной строке меняется корректно и кнопки «Назад»/»Вперед».

Практическая задача 3:
Добавьте в ваше приложение навигационное меню, используя компонент Link. Убедитесь, что переходы между страницами работают плавно, без перезагрузки всей страницы. Добавьте в меню четвертую ссылку, например, «Услуги», создайте для нее компонент Services и настройте соответствующий маршрут.

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

Часто нам нужно создавать страницы, контент которых зависит от некоторого идентификатора. Например, страница конкретного пользователя (/users/1/users/2), статьи в блоге (/posts/react-router-guide) или товара в каталоге. Для этого в React Router используются динамические сегменты в пути.

Мы определяем динамическую часть пути с помощью двоеточия (:). Например, path="/users/:userId". Здесь :userId это параметр, который может быть любой строкой.

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

Давайте создадим страницу профиля пользователя.

1. Добавим новый маршрут в App.jsx:

jsx
// ... остальные импорты
import UserProfile from './components/UserProfile';

function App() {
  return (
    <div className="App">
      {/* ... навигация ... */}

      <Routes>
        {/* ... другие маршруты ... */}

        {/* Динамический маршрут для профиля пользователя */}
        <Route path="/users/:userId" element={<UserProfile />} />
      </Routes>
    </div>
  );
}

2. Создадим компонент UserProfile.jsx:

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

function UserProfile() {
  // Хук useParams извлекает все параметры из URL
  const { userId } = useParams(); // Деструктуризируем объект, чтобы получить userId

  return (
    <div>
      <h2>Профиль пользователя</h2>
      {/* Отображаем userId, который был в URL */}
      <p>Вы просматриваете профиль пользователя с ID: <strong>{userId}</strong></p>
    </div>
  );
}

export default UserProfile;

Теперь, если вы перейдете по адресу http://localhost:3000/users/12345, вы увидите сообщение: «Вы просматриваете профиль пользователя с ID: 12345». А если перейдете на /users/maxim, то ID будет «maxim». В реальном приложении по этому userId мы бы сделали запрос к API, чтобы получить данные именно этого пользователя и отобразить его имя, аватар и т.д.

Практическая задача 4:
Создайте динамический маршрут для статей блога. Путь должен выглядеть как /blog/:postId. Создайте компонент BlogPost, который с помощью useParams получает postId и отображает его на странице. Добавьте в навигацию ссылку <Link to="/blog/my-first-post">Мой первый пост</Link> и убедитесь, что все работает.

Итоги и что дальше?

Вы только что сделали огромный шаг в освоении React. Вы узнали, что такое SPA, установили React Router, разобрались с основными компонентами BrowserRouterRoutesRoute и Link. Вы создали свое первое маршрутизируемое приложение с навигацией и даже попробовали работать с динамическими параметрами URL.

Это база, на которой строится 99% всех современных React-приложений. Вы уже можете создавать структуру для довольно сложных проектов с множеством страниц.

Это был 22-й урок из моего полного курса «React для начинающих». Если вы хотите структурировано, с нуля и до уверенного уровня освоить React, переходите по ссылке, там вас ждут все 30 уроков, домашние задания и дополнительные материалы. Удачи в изучении.

Поделиться статьей:
Поддержать автора блога

Поддержка автора осуществляется с помощью специальной формы ниже, предоставленной сервисом «ЮMoney». Все платёжные операции выполняются на защищённой странице сервиса, что обеспечивает их корректность и полную безопасность.

Персональные рекомендации
Оставить комментарий