React Router — это не то же самое, что маршрутизатор, направляющий сетевые данные — к счастью! Однако между сетевым маршрутизатором и React Router есть много общего. React Router помогает нам направлять пользователей к нужному компоненту. Например, мы можем использовать маршрутизацию на стороне клиента для создания одностраничного приложения (SPA), которое позволяет перемещаться между различными страницами без обновления браузера.

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

Но есть еще много интересного. В этой статье мы рассмотрим хуки React Router. Они позволяют разработчикам писать гораздо более чистый код, так как не нужно писать всю его шаблонную часть, как в компонентах класса. Из коробки мы можем получить доступ к нескольким хукам, таким как useHistory, useLocation, useParams и useRouteMatch. Например, хук useHistory дает нам доступ к объекту history для обработки изменений маршрута.

В этой статье будут рассмотрены следующие разделы:

  • Базовый пример React Router для начала работы. 

  • Примеры каждого хука React Router, чтобы понять для чего он используется

1. Начало работы: Настройка проекта React

Чтобы начать работу, нам нужно создать новое приложение React, выполнив в терминале следующую команду:

npx create-react-app react-router-tutorial

Эта команда создает новый проект под названием react-router-tutorial.

Теперь давайте изменим файл App.js.

import React, { Fragment } from "react";
import "./index.css"
export default function App() {
  return (
    <main>
      <nav>
        <ul>
          <li><a href="/">Home</a></li>
          <li><a href="/about">About</a></li>
          <li><a href="/shop">Shop</a></li>
        </ul>
        </nav>
     </main>
  );
}
// Home Page
const Home = () => (
  <Fragment>
    <h1>Home</h1>
    <LoremText />
  </Fragment>
);
// About Page
const About = () => (
  <Fragment>
    <h1>About</h1>
    <LoremText />
  </Fragment>
);
// Shop Page
const Shop = () => (
  <Fragment>
    <h1>Shop</h1>
    <LoremText />
  </Fragment>
);
const LoremText = () => (
  <p>
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
  </p>
)

Наш код определяет три различные страницы Home, About и Shop. Кроме того, мы добавили элемент <nav>, который позаботится о навигации. Если вы кликните на ссылку /about, то захотите перейти на страницу About. Для этого нам нужно добавить зависимость react-router-dom в наш проект. Выполните следующую команду в терминале:

npm install --save react-router-dom

Теперь давайте настроим наш React Router.

2. Настройка React Router

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

Обратите внимание, что мы также рефакторизовали теги <a> в теги <Link>, которые предлагает React Router. Прежде всего, они избавят вас от проблем с относительными и абсолютными путями, но, что более важно, тег <Link> не вызывает обновления страницы, в то время как тег <a>, естественно, это делает.

Кроме того, мы импортируем объект Route, чтобы определить соответствие между путем и компонентом. Например, путь /shop должен загрузить компонент Shop.

import React, { Fragment } from "react";
import "./index.css"
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
export default function App() {
  return (
    <Router>
      <main>
        <nav>
          <ul>
            <li><Link to="/">Home</Link></li>
            <li><Link to="/about">About</Link></li>
            <li><Link to="/shop">Shop</Link></li>
          </ul>
        </nav>

        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/shop">
            <Shop />
          </Route>
        </Switch>
      </main>
    </Router>
  );
}
// Home Page
const Home = () => (
  <Fragment>
    <h1>Home</h1>
    <LoremText />
  </Fragment>
);
// About Page
const About = () => (
  <Fragment>
    <h1>About</h1>
    <LoremText />
  </Fragment>
);
// Shop Page
const Shop = () => (
  <Fragment>
    <h1>Shop</h1>
    <LoremText />
  </Fragment>
);
const LoremText = () => (
  <p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
  </p>
)

Давайте проверим, подходит ли вам эта настройка, сначала создав проект:

npm run build`

Далее, давайте запустим проект и откроем браузер по адресу http://localhost:3000.

npm run start

Вы должны увидеть навигацию, заголовок и текст Lorem Ipsum. Обязательно проверьте, работает ли навигация.

Если все работает, далее изучим хуки React Router!

3. Настройка хуков React Router

Давайте рассмотрим, как мы можем использовать различные хуки React Router.

useHistory Первый хук useHistory позволяет нам получить доступ к объекту history. Затем мы можем вызывать такие методы объекта history, как goBack или push. Метод goBack позволяет перенаправить пользователя к предыдущему маршруту в стеке истории. Например, если пользователь перейдет со страницы Home на страницу Shop, а затем нажмет кнопку для возврата назад (“Go Back”), он снова будет перенаправлен на страницу Home.

С другой стороны, мы можем добавить новые записи в стек истории и заставить пользователя перейти на этот маршрут, используя метод push.

Вот окончательный код, реализующий кнопку "Go Back" для страницы About. Не забудьте импортировать хук useHistory из react-router.

import React, { Fragment } from "react";
import "./index.css"
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import { useHistory } from "react-router";
// About Page
const About = () => {
    const hist = useHistory();
    return (
        <div>
            <h1>About</h1>
            <button onClick={() => hist.goBack()}>Go Back</button>
            <LoremText />
        </div>
    );
};
export default function App() {
  return (
    <Router>
      <main>
        <nav>
          <ul>
            <li><Link to="/">Home</Link></li>
            <li><Link to="/about">About</Link></li>
            <li><Link to="/shop">Shop</Link></li>
          </ul>
        </nav>
        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/shop">
            <Shop />
          </Route>
        </Switch>
      </main>
    </Router>
  );
}
// Home Page
const Home = () => (
  <Fragment>
    <h1>Home</h1>
    <LoremText />
  </Fragment>
);
// Shop Page
const Shop = () => (
  <Fragment>
    <h1>Shop</h1>
    <LoremText />
  </Fragment>
);
const LoremText = () => (
  <p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
  </p>
)

useParams Из документации React находим следующее описание useParams:

useParams возвращает объект пары key/value (ключ/значение) параметров URL. Используйте его для доступа к match.params текущего <Route>.

Это гораздо более чистый способ доступа к параметрам URL. Чтобы проиллюстрировать это, давайте модифицируем страницу Shop, чтобы она принимала параметры URL и выводила полученный параметр на экран.

Первое, что мы сделали, это импортировали хук useParams. Затем мы должны изменить маршрут Shop, чтобы он принимал параметр URL. По умолчанию мы будем показывать ID первого товара при нажатии на тег Link из навигации. Можете видеть, что мы изменили путь для маршрута Shop на /shop/:id.

<Router>
      <main>
        <nav>
          <ul>
            <li><Link to="/">Home</Link></li>
            <li><Link to="/about">About</Link></li>
            <li><Link to="/shop/1">Shop</Link></li>
          </ul>
        </nav>
        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/shop/:id">
            <Shop />
          </Route>
        </Switch>
      </main>
    </Router>

Теперь можно получить доступ к параметру id через объект params, возвращаемый хуком useParams.

Кроме того, мы добавили возможность перехода к следующему ID товара с помощью кнопки с текстом Next product. Здесь используется метод push, открытый объектом history из предыдущего примера, чтобы поместить новую запись в стек history . Если воспользоваться кнопкой Go Back, то можно заметить, что вы будете перенаправлены к предыдущим идентификаторам товаров. Попробуйте!

import React, { Fragment } from "react";
import "./index.css"
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import { useHistory, useParams } from "react-router";
// About Page
const About = () => {
  const hist = useHistory();
  return (
    <div>
      <h1>About</h1>
      <button onClick={() => hist.goBack()}>Go Back</button>
      <LoremText />
    </div>
  );
};
const Shop = () => {
  const params = useParams();
  const current = params.id;
  const next = Number(current) + 1;
  const hist = useHistory();
  return (
    <div>
      <h1>Shop</h1>
      <p>You requested item with ID: {current}</p>
      <button onClick={() => hist.goBack()}>Go Back</button>
      <button onClick={() => hist.push(/shop/${next})}>Next product</button>
    </div>
  );
};
export default function App() {
  return (
    <Router>
      <main>
        <nav>
          <ul>
            <li><Link to="/">Home</Link></li>
            <li><Link to="/about">About</Link></li>
            <li><Link to="/shop/1">Shop</Link></li>
          </ul>
        </nav>
        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/shop/:id">
            <Shop />
          </Route>
        </Switch>
      </main>
    </Router>
  );
}
// Home Page
const Home = () => (
  <Fragment>
    <h1>Home</h1>
    <LoremText />
  </Fragment>
);
const LoremText = () => (
  <p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
  </p>
)

Вот и все для хука useParams React Router!

useLocation Хук useLocation возвращает объект location, представляющий текущий URL. Его можно рассматривать как useState, который возвращает новое местоположение при каждом изменении URL. Этот хук можно использовать, например, чтобы вызвать событие просмотра новой страницы для инструмента веб-аналитики.

Вот модифицированный пример компонента About, который печатает имя пути. Каждый раз, когда вы посещаете страницу About, путь /about будет выводиться в консоль.

import { useHistory, useLocation, useParams } from "react-router";
// About Page
const About = () => {
  const hist = useHistory();
  const location = useLocation();
  // Fictive call to Google Analytics
  // ga.send(["pageview", location.pathname])
  console.log(location.pathname);
  return (
    <div>
      <h1>About</h1>
      <button onClick={() => hist.goBack()}>Go Back</button>
      <LoremText />
    </div>
  );
};

Более того, мы можем даже получить параметры запроса с помощью хука маршрутизатора useLocation. Возвращаемый объект location содержит поле search, которое включает в себя строку запроса. Теперь, если вы используете простые строки запроса key=value (ключ=значение) и вам не нужна поддержка IE 11, можно использовать встроенный в браузер API URLSearchParams. Этот API предоставляет несколько методов, чтобы проверить, содержит ли строка запроса определенный ключ (с помощью функции has), но мы хотим получить параметр запроса с помощью функции get.

Давайте изменим Link для страницы About в нашем маршрутизаторе с /about на /about?param=text.

<Link to="/about?param=text">About</Link>

Теперь давайте получим значение параметра param с помощью приведенного ниже кода:

console.log(new URLSearchParams(location.search).get("param")); // result: "text"

Код функции About выглядит следующим образом.

// About Page
const About = () => {
  const hist = useHistory();
  const location = useLocation();
  // Fictive call to Google Analytics
  // ga.send(["pageview", location.pathname])
  console.log(location.pathname);
  console.log(location.search);
  console.log(new URLSearchParams(location.search).get("param")); // "text"
  return (
    <div>
      <h1>About</h1>
      <button onClick={() => hist.goBack()}>Go Back</button>
      <LoremText />
    </div>
  );
};

useRouteMatch Документация React Router определяет useRouteMatch следующим образом:

Хук useRouteMatch пытается сопоставить текущий URL таким же образом, как и <Route>. В основном он полезен для получения доступа к данным соответствия без фактического рендеринга <Route>.

В нашем примере мы будем использовать шаблон маршрута для страницы Shop. Если URL не содержит ID (/shop), то отобразятся все товары. В случае, если он содержит идентификатор (/shop/:id), то отображается конкретный продукт. Без хука соответствия маршрута необходимо использовать оператор Switch, чтобы отобразить обе страницы. Теперь проверим, какой маршрут совпадает, и вывести нужную страницу.

Сначала импортируйте хук useRouteMatch. Затем передадим хуку useRouteMatch путь, который необходимо проверить: /shop/:id. Если маршрут совпадает, откроется страница товара. Можно получить доступ к идентификатору продукта через routeMatch.params.id.

const Shop = () => {
  const hist = useHistory();
  const routeMatch = useRouteMatch("/shop/:id");
  // Use match to render the correct page
  return routeMatch ? (
    <div>
      <h1>Shop</h1>
      <p>You requested item with ID: {routeMatch.params.id}</p>
      <button onClick={() => hist.goBack()}>Go Back</button>
    </div>
  ) : (
    <div>
      <h1>Shop</h1>
      <p>All products</p>
      <br />
      <button onClick={() => hist.goBack()}>Go Back</button>
    </div>
  );
};

Теперь давайте изменим маршрутизатор, чтобы включить дополнительную ссылку, которая перенаправляет нас на страницу обзора Shop. Обе ссылки /shop и /shop/1 подключаются к компоненту <Shop /> без использования оператора Switch. Достаточно одного элемента Route, путь к которому установлен на /shop.

export default function App() {
  return (
    <Router>
      <main>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about?param=text">About</Link>
            </li>
            <li>
              <Link to="/shop">Shop</Link>
            </li>
            <li>
              <Link to="/shop/1">Product 1</Link>
            </li>
          </ul>
        </nav>
    &lt;Route path="/shop"&gt;
      &lt;Shop /&gt;
    &lt;/Route&gt;

    &lt;Switch&gt;
      &lt;Route exact path="/"&gt;
        &lt;Home /&gt;
      &lt;/Route&gt;
      &lt;Route path="/about"&gt;
        &lt;About /&gt;
      &lt;/Route&gt;
    &lt;/Switch&gt;
  &lt;/main&gt;
&lt;/Router&gt;

  );
}

Обеспечение наблюдения для приложений React в продакшене

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

OpenReplay помогает быстро найти первопричину, воспроизводя проблемы так, как будто они произошли в вашем собственном браузере. Он также отслеживает производительность фронтенда, фиксируя такие ключевые показатели, как время загрузки страницы, потребление памяти и медленные сетевые запросы, а также действия/состояние Redux.

Удачная отладка для современных фронтенд-команд — начните отслеживать работу вашего веб-приложения бесплатно.

Вот и все!

Если вы хотите узнать больше о хуках React Router, обязательно ознакомьтесь с примерами кода в документации по React Router.


Материал подготовлен в рамках курса «React.js Developer».

Всех желающих приглашаем на двухдневный онлайн-интенсив «React-hooks». React-hooks появились в React с версии 16.8, сегодня они используются уже повсеместно. За 2 вебинара мы разберемся, как работать с React-hooks, создадим компонент с использованием hooks, а также научимся делать кастомные hooks. Поработаем с react-testing-library и научимся тестировать компоненты и кастомные hooks. Зарегистрироваться можно здесь.

Комментарии (0)