Главная | Все статьи | Код

Маршрутизация в React Router: как она работает и почему ее выбирают разработчики

Время чтения статьи ~10 минут
Маршрутизация в React Router: как она работает и почему ее выбирают разработчики главное изображение

React Router — решение для переключения и маршрутизации страниц React. Библиотека появилась еще в 2014 году и стала одним из первых популярных проектов с открытым исходным кодом на основе React. Рассказываем про ключевую концепцию библиотеки и разбираем, почему разработчики выбирают именно ее для организации маршрутизации. Бонусом — напишем небольшое приложение c использованием хуков useLocation и useNavigate, и увидим, как на практике работает маршрутизация через React Router.

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

Познакомьтесь с Фронтенд разработкой бесплатно

Начните с этих 5 уроков

Декларативная маршрутизация

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

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

Что делает React Router лучшим по сравнению с другими библиотеками маршрутизации:

  • Декларативное определение маршрута (с использованием JSX внутри компонентов React)
  • Отраслевой стандарт
  • Библиотека предлагает множество примеров кода и обширное онлайн-руководство
  • React Router предоставляет возможность использования хуков и функциональных компонентов.

Текущая версия React Router v6 внесла ряд ключевых изменений по сравнению с предыдущей версией v5:

  • Компонент <Switch> был переименован в <Routes>
  • Хук useRoutes() заменяет react-router-config для определения маршрутов как простых объектов
  • Каждый дочерний компонент <Routes> должен быть <Route>, что может нарушить некоторые предыдущие методы организации и составления маршрутов.

В нашей статье все примеры будут построены c использованием React Router именно шестой версии.

Подготовка окружения

Прежде чем мы приступим к практике, необходимо подготовить рабочее окружение.

Для создания веб-приложений нам нужен react-router-dom, который включает в себя все, что есть в react-router, и добавляет несколько специфичных для DOM API компонентов, включая <BrowserRouter> и <Link>.

Создадим проект с помощью Create React App и установим react-router-dom:

npx create-react-app my-app
cd my-app
npm i react-router-dom

Кроме того, установим lorem-ipsum для генерации текста-заполнителя lorem ipsum для страниц.

npm i lorem-ipsum

Теперь пакеты react-router-dom и lorem-ipsum можно увидеть в package.json в качестве зависимостей.

Настройка маршрутов

Router — это компонент верхнего уровня с отслеживанием состояния, который заставляет работать все остальные компоненты навигации и хуки. В React Router есть BrowserRouter, HashRouter, StaticRouter, NativeRouter и MemoryRouter. Для веб-приложений обычно используется BrowserRouter. Приложение должно иметь один <BrowserRouter>, который обертывает один или несколько <Routes>.

<Routes> проверяет все свои дочерние элементы <Route>, чтобы найти наилучшее соответствие, и отображает эту часть пользовательского интерфейса.

<Route> можно определить либо как объект, либо элемент Route. Если это объект, он имеет форму { path, element }. Если это элемент Route, компонент имеет вид <Route path element>. Когда указанный путь path соответствует текущему URL-адресу, то отображается компонент, указанный в качестве элемента element. В нашем приложении мы будем использовать именно element.

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

// src/Components/Pages.jsx
import { loremIpsum } from 'lorem-ipsum';

const BuildPage = (index) => (
  <>
    <h3>Page {index}</h3>
    <div>
      Page {index} content: { loremIpsum({ count: 5 })}
    </div>
  </>
);

export const PageOne = () => BuildPage(1);
export const PageTwo = () => BuildPage(2);

В src/App.js создадим два маршрута:

// App.jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { PageOne, PageTwo } from './Components/Pages';

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="one" element={<PageOne />} />
        <Route path="two" element={<PageTwo />} />
      </Routes>
    </BrowserRouter>
  );
}

export default App;

В этом коде <BrowserRouter> и <Routes> используются для определения маршрутизатора.

В приложении есть два <Route>. Когда URL-адрес соответствует пути one, приложение показывает компонент PageOne. Когда URL-адрес соответствует пути two, приложение показывает компонент PageTwo.

Запустим приложение, выполнив команду npm start.

http://localhost:3000/one показывает PageOne

PageOne

http://localhost:3000/two показывает PageTwo

PageTwo

Приложение работает для путей one и two. Однако http://localhost:3000 ничего не показывает, как и любые недействительные URL-адреса, такие как http://localhost:3000/anything.

Эту проблему можно решить с помощью подстановочного пути:

// App.jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { PageOne, PageTwo } from './Components/Pages';

function App() {
  return (
    <BrowserRouter>
      <Routes>
        {/* подстановочный путь */}
        <Route path="*" element={<PageOne />} />
        <Route path="two" element={<PageTwo />} />
      </Routes>
    </BrowserRouter>
  );
}

export default App;

Теперь http://localhost:3000/two показывает PageTwo. Во всех остальных случаях будет отображаться PageOne. Порядок указания маршрутов не имеет значения, так как React Router 6 выбирает в первую очередь наиболее точное совпадение.

Читайте также: Наталия Давыдова, фронтенд-разработчица в «Точке»: как мое комьюнити помогает джунам найти работу

Познакомьтесь с Фронтенд разработкой бесплатно

Начните с этих 5 уроков

Настройка вложенных маршрутов в React Router v6

Два маршрута в приведенном выше примере работают, как мы и ожидали. Однако вводить URL-адрес в адресной строке браузера неудобно. Мы хотели бы иметь возможность навигации по ссылке, которая называется <Link>.

<Link> отображает доступный элемент <a> с реальным href, указывающим на ресурс, на который он ссылается. Клик по ссылке устанавливает URL-адрес и отслеживает историю просмотров.

Создадим главную страницу, которая будет содержать ссылки <Link> на соответствующие страницы src/MainPage.js.

// src/MainPage.js
import { Link } from 'react-router-dom';

export const MainPage = () => (
  <nav>
    <ul>
      <li>
        <Link to="/one">Page One</Link>
      </li>
      <li>
        <Link to="/two">Page Two</Link>
      </li>
    </ul>
  </nav>
);

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

import {
  BrowserRouter,
  Routes,
  Route,
} from 'react-router-dom';
import { PageOne, PageTwo } from './Components/Pages';
import MainPage from './Components/MainPage';

const App = () => {

  return (
    <BrowserRouter>
      <Routes>
          <Route path="/" element={<MainPage/> } />
          <Route path="one" element={<PageOne />} />
          <Route path="two" element={<PageTwo />} />
      </Routes>
    </BrowserRouter>
  );
}

export default App;

Теперь, когда URL-адрес соответствует «/», приложение будет показывать MainPage: MainPage.

Нажав на ссылку Page One, мы перейдем на PageOne. Нажав на ссылку Page Two, мы перейдем на PageTwo

Нажав на ссылку Page One, мы перейдем на PageOne. Нажав на ссылку Page Two, мы перейдем на PageTwo.

Однако внутри PageOne или PageTwo мы не можем использовать ссылки для навигации. Чтобы решить эту проблему, создадим компонент <Outlet> в MainPage. Он позволяет отображать вложенный пользовательский интерфейс при отображении дочерних маршрутов. Таким образом, при клике на 'one' будет отображаться компонент PageOne, а при клике на 'two'PageTwo.

Это src/MainPage.js с Outlet:

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

const MainPage = () => {
  return (
    <>
      <nav>
        <ul>
          <li>
            <Link to='/one'>Page One</Link>
          </li>
          <li>
            <Link to='/two'>Page Two</Link>
          </li>
        </ul>
      </nav>
      <hr />
      <Outlet />
    </>
  )
};

<Outlet> вызывает вложенные маршруты, где у каждого могут быть свои дочерние маршруты, занимающие часть URL-адреса. Вложенные маршруты обычно описывают через относительные ссылки.

Вот модифицированный src/App.jsx:

import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { PageOne, PageTwo } from './Components/Pages';
import MainPage from './Components/MainPage';

const App = () => {

  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<MainPage />} >
            <Route index element={<div>No page is selected.</div> } />
            <Route path="one" element={<PageOne />} />
            <Route path="two" element={<PageTwo />} />
          </Route>
      </Routes>
    </BrowserRouter>
  );
}

http://localhost:3000/ теперь выглядит так:

localhost:3000

А так http://localhost:3000/one:

localhost:3000/one

Хук useLocation

Местоположение — это объект, который представляет местоположение URL. Он основан на объекте браузера window.location.

Хук useLocation возвращает объект текущего местоположения.

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

const MainPage = () => {
  const location = useLocation();

  useEffect(() => {
    console.log('Current location is ', location);
  }, [location]);

  return (
    <>
      <nav>
        <ul>
          <li>
            <Link to="/one">Page One</Link>
          </li>
          <li>
            <Link to="/two">Page Two</Link>
          </li>
        </ul>
      </nav>
      <hr />
      <Outlet />
    </>
  );
};

В переменную location мы сохраняем местоположение, которое генерируется хуком useLocation. Внутри хука useEffect мы будем выводить текущее местоположение при каждом изменении параметра location.

Если URL-адрес http://localhost:3000/, то консоль регистрирует:

Current location is {pathname: '/', search: '', hash: '', state: null, key: 'default'}

Если URL-адрес http://localhost:3000/one, то консоль регистрирует:

Current location is {pathname: '/one', search: '', hash: '', state: null, key: 'f2114bru'}

Когда URL-адрес http://localhost:3000/anything, то тут консоль регистрирует:

Current location is {pathname: '/anything', search: '', hash: '', state: null, key: 'default'}

Хук useNavigate

Хук useNavigate возвращает функцию, которую можно использовать для программной навигации. Заменим все <Link> в нашем приложении на <button> в src/MainPage.js:

import React, { useEffect } from 'react';
import { Outlet, useLocation, useNavigate } from 'react-router-dom';

const MainPage = () => {
  const location = useLocation();
  const navigate = useNavigate();

  useEffect(() => {
    console.log('Current location is ', location);
  }, [location]);

  return (
    <>
      <nav>
        <ul>
          <li>
            <button onClick={() => navigate('one', { replace: false })}>
              Page One
            </button>
          </li>
          <li>
            <button onClick={() => navigate('two', { replace: false })}>
              Page Two
            </button>
          </li>
        </ul>
      </nav>
      <hr />
      <Outlet />
    </>
  )
};

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

useNavigate

Хук useNavigate может принимать:

  1. Либо значение To с необязательным вторым аргументом { replace, state }, как это работает и в <Link to>
  2. Либо дельта-число, чтобы войти в стек истории. Например, навигация (-1) по своей сути аналогична нажатию кнопки «Назад».

Другие хуки

Мы показали, как использовать useLocation и useNavigate. При этом в библиотеке есть и другие хуки — например, useParams или useRoutes. С ними можно ознакомиться в официальной документации React Router.

Статья является адаптированным переводом команды Хекслета материалов из следующих источников: 1 и 2.

Профессия «Фронтенд-разработчик»
  • Изучите востребованные JavaScript и TypeScript
  • Научитесь создавать пользовательские интерфейсы сайтов и приложений
  • Освойте самый популярный фреймворк JavaScript — React
  • Познакомьтесь с языками веб-разработки HTML и CSS
Узнать больше

Аватар пользователя Olga Pejenkova
Olga Pejenkova 07 февраля 2023
36
Рекомендуемые программы
профессия
от 6 300 ₽ в месяц
Разработка фронтенд-компонентов для веб-приложений
10 месяцев
с нуля
Старт 25 апреля
профессия
от 6 300 ₽ в месяц
Разработка веб-приложений на Django
10 месяцев
с нуля
Старт 25 апреля
профессия
от 6 183 ₽ в месяц
Ручное тестирование веб-приложений
4 месяца
с нуля
Старт 25 апреля
профессия
от 6 300 ₽ в месяц
Разработка приложений на языке Java
10 месяцев
с нуля
Старт 25 апреля
профессия
от 5 025 ₽ в месяц
новый
Сбор, анализ и интерпретация данных
9 месяцев
с нуля
Старт 25 апреля
профессия
от 6 300 ₽ в месяц
Разработка веб-приложений на Laravel
10 месяцев
с нуля
Старт 25 апреля
профессия
от 5 840 ₽ в месяц
Создание веб-приложений со скоростью света
5 месяцев
c опытом
Старт 25 апреля
профессия
от 9 900 ₽ в месяц
Разработка фронтенд- и бэкенд-компонентов для веб-приложений
16 месяцев
с нуля
Старт 25 апреля
профессия
от 6 300 ₽ в месяц
Разработка бэкенд-компонентов для веб-приложений
10 месяцев
с нуля
Старт 25 апреля
профессия
новый
Автоматизированное тестирование веб-приложений на JavaScript
8 месяцев
c опытом
в разработке
Старт 25 апреля
профессия
Верстка с использованием последних стандартов CSS
5 месяцев
с нуля
Старт в любое время