Подписывайтесь на мой твиттер, там всегда что-нибудь интересное!

Подробно о React Router. Часть 2 — параметры запроса, 404 страница и передача пропсов компоненту роута

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

Первая часть — Подробно о React Router. Часть 1 — основы роутинга, типы и динамические страницы

p.s статья описывает самый распространенную версию роутера на данный момент. 6я версия сейчас находится в альфе и имеет кардинально другой синтаксис.

Перевод серии статей Deep dive into React Router

Работа с параметрами запроса в React Router

В третьей части мы узнали о технике создания динамических ссылок в React Router. В этой части, мы узнаем о работе с параметрами запроса в URL.

Давайте создадим новый Route для страницы поиска с параметрами запроса.

// App.js
...
const SearchPage = () => {  return (    <h3>Search Page</h3>  );}
const App = () => {
  return (
    <section className="App">
      <Router>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
        <Link to="/users">Users</Link>
        <Link to="/search?q=react">Search</Link>        
        <Route exact path="/" component={IndexPage} />
        <Route exact path="/users" component={UsersPage} />
        <Route exact path="/user/:userId" component={UserPage} />
        <Route exact path="/about" component={AboutPage} />
        <Route exact path="/search" component={SearchPage} /> 
      </Router>
      <a href="/about">about with browser reload</a>
    </section>
  );
};

...

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

Нам нет нужды в указании параметров запроса в роуте, так как по дефолту они будут обработаны React роутером и отправлены в пропсы компонента.

Если мы укажем вот так /search/?q=:searchValue, то это не сработает в роутере. Вы можете попробовать это в песочнице, роутер этого не поймет.

Теперь нам нужно получить доступ ко всем параметрам запросов, которые мы передаём URL в компоненте. В предыдущей части мы говорили о том как роутер передаёт два параметра match и location.

Для динамических роутов, React роутер передаёт параметры в пропсы match.

Для роутов с параметрами запроса, инфо о них будет отправлено через location пропсы.

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

...

const SearchPage = ({ match, location }) => {
  return (
    <p>
      <strong>Location Props: </strong>
      {JSON.stringify(location, null, 2)}
    </p>
  );
}

...

Как вы видите на странице поиска:

Location Props: { "pathname": "/search", "search": "?q=react", "hash": "", "key": "allc40" }

Роутер передаёт параметры запроса в location.search. Если вы добавите больше параметров запросов, то получите все тоже самое в той же строке, например ?q=react&limit=3.

Чтобы получить каждое значение отдельно, вы могли бы использовать вашу собственную библиотеку помощник или пакет npm, который умеет работать с параметрами запроса таким образом, чтобы вы получали каждое значение парой ключ-значение, но уже в отдельном объекте.

Например, есть пакет yarn add qs. Но существует огромное количество альтернатив ему. Вы можете смело выбрать тот, который подходит вашему URL.

Давайте покажем значение location.search в компоненте.

const SearchPage = ({ match, location }) => {
  return (
    <p>
      <strong>Query Params: </strong>
      {location.search}
    </p>
  );
};

Посмотреть результат

Создаем страницу 404

Мы уже узнали как создавать динамические URL и как работать с параметрами запросов в React роутере, а также как передавать эти значения странице компонента.

В этой части мы узнаем о том, как показать 404 страницу, в том случае, если нет подходящего роута для URL.

Запустите приложение и зайдите на любую несуществующую страницу, например /404-not-found.

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

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

// App.js

...

const NoMatchPage = () => {return (<h3>404 - Not found</h3>);};
const App = () => {
  return (
    <section className="App">
      <Router>
        ...
        <Link to="/users">Users</Link>
        <Link to="/search?q=react">Search</Link>
        ...
        <Route exact path="/about" component={AboutPage} />
        <Route exact path="/search" component={SearchPage} />
        <Route component={NoMatchPage} />      
      </Router>
    </section>
  );
};

...

Зайдите на любой рандомный URL и вы увидите простую 404ю страницу. Но подождите, давайте проверим будут ли работать другие страницы. А оказывается нет.

Зайдите по ссылке about, вы увидите, что отрендерится и страница about, и 404-я страница. Это произойдёт так как точный роут совпадет с /about и потом он пробежится до конца всех указанных роутов и загрузит 404 роут, так как у него не указан путь.

В React роутере есть компонент под названием Switch, чтобы остановиться на одном роуте и исключить загрузку любых других роут компонентов после него.

Тут все очень просто. Оберните все роуты в Switch. Давайте это сделаем правильно и сделаем 404-ю страницу без ошибок.

// App.js

...
import { Link, BrowserRouter as Router, Route, Switch } from 'react-router-dom';...

const App = () => {
  return (
    <section className="App">
      <Router>
        ...
        <Switch>          
          <Route exact path="/" component={IndexPage} />
          ...
          <Route exact path="/search" component={SearchPage} />
          <Route component={NoMatchPage} />
        </Switch>      
      </Router>
    </section>
  );
};

...

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

Пример

Передаём пропсы компоненту роута

Мы видели несколько примеров и рабочих кейсов в React роутере. Один из них это передача пропсов напрямую компоненту роута.

В React роутере это очень легко сделать. Давайте создадим новый роут для передачи его компоненту пропсов.

// App.js
...

const PropsPage = () => {  return (    <h3>Props Page</h3>  );};
const App = () => {
  return (
    <section className="App">
      <Router>
        ...
        <Link to="/404-not-found">404</Link>
        <Link to="/props">Passing Props</Link>        
        <Switch>
          ...
          <Route exact path="/props" component={PropsPage} /> 
          <Route component={NoMatchPage} />
        </Switch>
      </Router>
      <a href="/about">about with browser reload</a>
    </section>
  );
};

export default App;

Прямо сейчас мы добавили компонент и новую страницу. Давайте передарим пропс title уже самой странице.

Есть два способа это сделать. Первый очень простой.

Передаём функцию как пропс компонента в компоненте роута

...

const PropsPage = ({ title }) => {
  return (
    <h3>{title}</h3>
  );
};

...

<Route exact path="/props-through-component" component={() => <PropsPage title={`Props through component`} />} />

Эта штука сработает, но такой подход не рекомендуется применять с роутером. Почему? Сейчас объясню.

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

В маленьких приложениях, с довольно не сложными страницами, это не будет влиять на производительность. Но для больших приложений с множественным измнением состояния на странице, это снизит производительность страницы из-за необязательного ре-рендеринга.

Роутер даёт нам простое решение в этой ситуации. Вместо простой передачи функции через пропсы component, мы можем передать их через пропсы render. Передавая наши пропсы, нам также надо передать дефолтные пропсы пропсам рендеринга. Давайте посмотрим пример:

...

<Link to="/props-through-render">Props through render</Link>
...

<Route exact path="/props-through-render" render={(props) => <PropsPage {...props} title={`Props through render`} />} />

Вот это уже рекомендуемый способ передачи самих пропсов в React роутере.

Это очень просто. Если вы хотите увидеть весь пример, то проверьте его тут.