React Router — это стандартная библиотека маршрутизации для React. Когда вам нужно перемещаться по приложению React с несколькими представлениями, вам понадобится маршрутизатор для управления URL-адресами. React Router делает это, поддерживая синхронизацию пользовательского интерфейса приложения и URL-адреса.
Это руководство знакомит вас с React Router v4 и тем, что вы можете с ним сделать.
Вступление
React — это популярная библиотека для создания одностраничных приложений (SPA), которые отображаются на стороне клиента. SPA может иметь несколько представлений (или страниц ), и в отличие от обычных многостраничных приложений, перемещение по этим представлениям не должно приводить к перезагрузке всей страницы. Вместо этого мы хотим, чтобы представления были встроены в пределах текущей страницы. Конечный пользователь, привыкший к многостраничным приложениям, ожидает, что в SPA будут присутствовать следующие функции:
-  Каждое представление в приложении должно иметь URL, который однозначно определяет это представление.  Это сделано для того, чтобы пользователь мог добавить в закладки URL-адрес для последующего использования, например, 
www.example.com/products. - Кнопка браузера «назад» и «вперед» должна работать как положено.
 -   Динамически сгенерированные вложенные представления также должны иметь собственный URL-адрес — например, 
example.com/products/shoes/101, где 101 — идентификатор продукта. 
Маршрутизация — это процесс синхронизации URL браузера с тем, что отображается на странице. React Router позволяет обрабатывать маршрутизацию декларативно . Подход декларативной маршрутизации позволяет вам контролировать поток данных в вашем приложении, говоря «маршрут должен выглядеть следующим образом»:
 <Route path="/about" component={About}/> 
  Вы можете разместить компонент <Route> любом месте, где вы хотите, чтобы ваш маршрут отображался.  Поскольку <Route> , <Link> и все другие API React Router, с которыми мы будем иметь дело, являются лишь компонентами, вы легко можете привыкнуть к маршрутизации в React. 
Примечание, прежде чем начать. Существует распространенное заблуждение, что React Router является официальным решением для маршрутизации, разработанным Facebook. На самом деле это сторонняя библиотека, которая широко популярна благодаря своему дизайну и простоте. Если ваши требования ограничены маршрутизаторами для навигации, вы можете реализовать собственный маршрутизатор с нуля без особых хлопот. Однако понимание того, как основы React Router помогут вам лучше понять, как должен работать маршрутизатор.
обзор

- базовая навигационная маршрутизация
 - вложенная маршрутизация
 - вложенная маршрутизация с параметрами пути
 - защищенная маршрутизация
 
  Все концепции, связанные со строительством этих маршрутов, будут обсуждаться по пути.  Весь код проекта доступен на этом репозитории GitHub .  Как только вы окажетесь в определенном демонстрационном каталоге, запустите npm install для установки зависимостей.  Чтобы разместить приложение на сервере разработки, запустите npm start и npm start по http://localhost:3000/ чтобы увидеть демонстрацию в действии. 
Давайте начнем!
Настройка React Router
Я предполагаю, что у вас уже есть среда разработки. Если нет, перейдите к разделу « Начало работы с React и JSX ». Кроме того, вы можете использовать Create React App для создания файлов, необходимых для создания базового проекта React. Это структура каталогов по умолчанию, созданная приложением Create React:
  react-routing-demo-v4 ├── .gitignore ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── README.md ├── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ └── registerServiceWorker.js └── yarn.lock 
  Библиотека React Router состоит из трех пакетов: react-router , react-router-dom и react-router-native .  react-router является базовым пакетом для маршрутизатора, в то время как два других зависят от среды.  Вы должны использовать react-router-dom если вы создаете веб-сайт, и react-router-native если вы находитесь в среде разработки мобильных приложений с использованием React Native. 
  Используйте npm для установки react-router-dom : 
 npm install --save react-router-dom 
Основы React Router
Вот пример того, как будут выглядеть наши маршруты:
 <Router> <Route exact path="/" component={Home}/> <Route path="/category" component={Category}/> <Route path="/login" component={Login}/> <Route path="/products" component={Products}/> </Router> 
маршрутизатор
Вам нужен компонент маршрутизатора и несколько компонентов маршрута, чтобы настроить базовый маршрут, как показано выше. Поскольку мы создаем приложение на основе браузера, мы можем использовать два типа маршрутизаторов из React Router API:
-  
<BrowserRouter> -  
<HashRouter> 
Основное различие между ними проявляется в URL, которые они создают:
 // <BrowserRouter> http://example.com/about // <HashRouter> http://example.com/#/about 
  <BrowserRouter> более популярен среди двух, потому что он использует API истории HTML5 для отслеживания истории вашего маршрутизатора.  <HashRouter> , с другой стороны, использует хеш-часть URL ( window.location.hash ) для запоминания вещей.  Если вы намереваетесь поддерживать устаревшие браузеры, вам следует придерживаться <HashRouter> . 
  Оберните компонент <BrowserRouter> вокруг компонента App. 
index.js
 /* Import statements */ import React from 'react'; import ReactDOM from 'react-dom'; /* App is the entry point to the React code.*/ import App from './App'; /* import BrowserRouter from 'react-router-dom' */ import { BrowserRouter } from 'react-router-dom'; ReactDOM.render( <BrowserRouter> <App /> </BrowserRouter> , document.getElementById('root')); 
Примечание. Компонент маршрутизатора может иметь только один дочерний элемент. Дочерний элемент может быть элементом HTML — например, div — или компонентом реакции.
  Чтобы React Router работал, вам нужно импортировать соответствующий API из библиотекиact react-router-dom .  Здесь я импортировал BrowserRouter в index.js .  Я также импортировал компонент App из App.js  App.js , как вы уже догадались, является точкой входа в компоненты React. 
Приведенный выше код создает экземпляр истории для всего нашего компонента приложения. Позвольте мне официально познакомить вас с историей.
история
history— это библиотека JavaScript, которая позволяет вам легко управлять историей сеансов везде, где работает JavaScript. history предоставляет минимальный API, который позволяет вам управлять стеком истории, перемещаться, подтверждать навигацию и сохранять состояние между сеансами. — Реагировать на учебные документы
  Каждый компонент маршрутизатора создает объект истории, который отслеживает текущее местоположение ( history.location ), а также предыдущие местоположения в стеке.  Когда текущее местоположение изменяется, представление повторно визуализируется, и вы получаете ощущение навигации.  Как меняется текущее местоположение?  У объекта истории есть методы, такие как history.push() и history.replace() чтобы позаботиться об этом.  history.push() вызывается при нажатии на компонент <Link> , а history.replace() вызывается при использовании <Redirect> .  Другие методы — такие как history.goBack() и history.goForward() — используются для навигации по стеку истории при переходе назад или вперед страницы. 
Двигаясь дальше, у нас есть ссылки и маршруты.
Ссылки и маршруты
  Компонент <Route> является наиболее важным компонентом в маршрутизаторе React.  Он отображает некоторый пользовательский интерфейс, если текущее местоположение соответствует пути маршрута.  В идеале, компонент <Route> должен иметь пропущенный path , и если имя пути совпадает с текущим местоположением, он отображается. 
  Компонент <Link> , с другой стороны, используется для навигации между страницами.  Это сопоставимо с элементом привязки HTML.  Однако использование якорных ссылок приведет к обновлению браузера, что нам не нужно.  Таким образом, вместо этого мы можем использовать <Link> для перехода к определенному URL-адресу и перерисовать представление без обновления браузера. 
Мы рассмотрели все, что вам нужно знать для создания базового маршрутизатора. Давайте построим один.
Демо 1: базовая маршрутизация
SRC / App.js
 /* Import statements */ import React, { Component } from 'react'; import { Link, Route, Switch } from 'react-router-dom'; /* Home component */ const Home = () => ( <div> <h2>Home</h2> </div> ) /* Category component */ const Category = () => ( <div> <h2>Category</h2> </div> ) /* Products component */ const Products = () => ( <div> <h2>Products</h2> </div> ) /* App component */ class App extends React.Component { render() { return ( <div> <nav className="navbar navbar-light"> <ul className="nav navbar-nav"> /* Link components are used for linking to other views */ <li><Link to="/">Homes</Link></li> <li><Link to="/category">Category</Link></li> <li><Link to="/products">Products</Link></li> </ul> </nav> /* Route components are rendered if the path prop matches the current URL */ <Route path="/" component={Home}/> <Route path="/category" component={Category}/> <Route path="/products" component={Products}/> </div> ) } } 
  Мы объявили компоненты для дома, категории и продуктов внутри App.js  Хотя пока это нормально, когда компонент начинает расти, лучше иметь отдельный файл для каждого компонента.  Как правило, я обычно создаю новый файл для компонента, если он занимает более 10 строк кода.  Начиная со второй демонстрации, я буду создавать отдельный файл для компонентов, размер которых слишком велик для размещения внутри файла App.js 
  Внутри компонента App мы написали логику маршрутизации.  Путь <Route> совпадает с текущим местоположением, и компонент визуализируется.  Компонент, который должен быть визуализирован, передается как вторая опора. 
  Здесь / соответствует и / и /category .  Таким образом, оба маршрута сопоставляются и отображаются.  Как мы этого избежать?  Вы должны передать exact= {true} реквизиты exact= {true} маршрутизатору с path='/' : 
 <Route exact={true} path="/" component={Home}/> 
Если вы хотите, чтобы маршрут отображался только в том случае, если пути в точности совпадают, вы должны использовать точные реквизиты.
Вложенная маршрутизация
  Чтобы создать вложенные маршруты, нам нужно лучше понять, как работает <Route> .  Давайте сделаем это. 
  <Route> есть три реквизита, которые вы можете использовать для определения того, что визуализируется: 
-   компонент .  Мы уже видели это в действии.  Когда URL совпадает, маршрутизатор создает элемент React из данного компонента, используя 
React.createElement. - визуализация . Это удобно для встроенного рендеринга. Пропеллер рендеринга ожидает функцию, которая возвращает элемент, когда местоположение соответствует пути маршрута.
 - дети Прописка children похожа на визуализацию в том, что она ожидает функцию, которая возвращает элемент React. Тем не менее, дочерние элементы визуализируются независимо от того, соответствует ли путь местоположению или нет.
 
Путь и матч
Путь используется для определения части URL, которой должен соответствовать маршрутизатор. Он использует библиотеку Path-to-RegExp, чтобы превратить строку пути в регулярное выражение. Затем он будет сопоставлен с текущим местоположением.
Если путь и местоположение маршрутизатора успешно сопоставлены, создается объект, и мы называем его объектом сопоставления . Объект соответствия содержит больше информации об URL-адресе и пути. Эта информация доступна через ее свойства, перечисленные ниже:
-   
match.url. Строка, которая возвращает совпавшую часть URL. Это особенно полезно для создания вложенных<Link> -   
match.pathСтрока, которая возвращает строку пути маршрута, то есть<Route path="">. Мы будем использовать это для создания вложенных<Route>s. -   
match.isExact. Логическое значение, которое возвращает true, если совпадение было точным (без конечных символов). -   
match.params. Объект, содержащий пары ключ / значение из URL, проанализированного пакетом Path-to-RegExp. 
  Теперь, когда мы знаем все о <Route> , давайте создадим маршрутизатор с вложенными маршрутами. 
Переключатель компонентов
  Прежде чем мы приступим к демонстрационному коду, я хочу познакомить вас с компонентом <Switch> .  Когда несколько <Route> используются вместе, все подходящие маршруты отображаются включительно.  Рассмотрим этот код из демонстрации 1. Я добавил новый маршрут, чтобы продемонстрировать, почему <Switch> полезен. 
 <Route exact path="/" component={Home}/> <Route path="/products" component={Products}/> <Route path="/category" component={Category}/> <Route path="/:id" render = {()=> (<p> I want this text to show up for all routes other than '/', '/products' and '/category' </p>)}/> 
  Если URL-адрес /products , отображаются все маршруты, соответствующие местоположению /products .  Таким образом, <Route> with path :id отображается вместе с компонентом Products .  Это по замыслу.  Однако, если это не то поведение, которое вы ожидаете, вы должны добавить компонент <Switch> в ваши маршруты.  При использовании <Switch> отображается только первый дочерний <Route> который соответствует расположению. 
Демонстрация 2: вложенная маршрутизация
  Ранее мы создали маршруты для / , /category и /products .  Что делать, если мы хотели URL-адрес формы /category/shoes ? 
SRC / App.js
 import React, { Component } from 'react'; import { Link, Route, Switch } from 'react-router-dom'; import Category from './Category'; class App extends Component { render() { return ( <div> <nav className="navbar navbar-light"> <ul className="nav navbar-nav"> <li><Link to="/">Homes</Link></li> <li><Link to="/category">Category</Link></li> <li><Link to="/products">Products</Link></li> </ul> </nav> <Switch> <Route exact path="/" component={Home}/> <Route path="/category" component={Category}/> <Route path="/products" component={Products}/> </Switch> </div> ); } } export default App; /* Code for Home and Products component omitted for brevity */ 
  В отличие от более ранней версии React Router, в версии 4 вложенные <Route> должны предпочтительно входить в родительский компонент.  То есть компонент Category является здесь родителем, и мы будем объявлять маршруты для category/:name внутри родительского компонента. 
SRC / Category.jsx
 import React from 'react'; import { Link, Route } from 'react-router-dom'; const Category = ({ match }) => { return( <div> <ul> <li><Link to={`${match.url}/shoes`}>Shoes</Link></li> <li><Link to={`${match.url}/boots`}>Boots</Link></li> <li><Link to={`${match.url}/footwear`}>Footwear</Link></li> </ul> <Route path={`${match.path}/:name`} render= {({match}) =>( <div> <h3> {match.params.name} </h3></div>)}/> </div>) } export default Category; 
  Во-первых, мы объявили пару ссылок для вложенных маршрутов.  Как упоминалось ранее, match.url будет использоваться для создания вложенных ссылок и match.path для вложенных маршрутов.  Если у вас возникают проблемы с пониманием концепции соответствия, console.log(match) предоставляет некоторую полезную информацию, которая может помочь прояснить ее. 
 <Route path={`${match.path}/:name`} render= {({match}) =>( <div> <h3> {match.params.name} </h3></div>)}/> 
  Это наша первая попытка динамической маршрутизации.  Вместо жесткого кодирования маршрутов мы использовали переменную внутри пути.  :name является параметром пути и перехватывает все после category/ до следующей косой черты.  Таким образом, путь, например products/running-shoes , создаст объект params следующим образом: 
 { name: 'running-shoes' } 
  Захваченные данные должны быть доступны по адресу match.params или props.match.params зависимости от того, как передаются реквизиты.  Другая интересная вещь заключается в том, что мы использовали render реквизит.  реквизит render очень удобен для встроенных функций, которые не требуют собственного компонента. 
Демонстрация 3: Вложенная маршрутизация с параметрами Path
Давайте все усложним немного, не так ли? Реальный маршрутизатор будет иметь дело с данными и отображать их динамически. Предположим, что у нас есть данные о продукте, возвращаемые серверным API в форме ниже.
SRC / Products.jsx
 const productData = [ { id: 1, name: 'NIKE Liteforce Blue Sneakers', description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin molestie.', status: 'Available' }, { id: 2, name: 'Stylised Flip Flops and Slippers', description: 'Mauris finibus, massa eu tempor volutpat, magna dolor euismod dolor.', status: 'Out of Stock' }, { id: 3, name: 'ADIDAS Adispree Running Shoes', description: 'Maecenas condimentum porttitor auctor. Maecenas viverra fringilla felis, eu pretium.', status: 'Available' }, { id: 4, name: 'ADIDAS Mid Sneakers', description: 'Ut hendrerit venenatis lacus, vel lacinia ipsum fermentum vel. Cras.', status: 'Out of Stock' }, ]; 
Нам нужно создать маршруты для следующих путей:
-   
/products. Это должно отобразить список продуктов. -   
/products/:productId. Если продукт с:productIdсуществует, он должен отображать данные продукта, а если нет, то должен отображать сообщение об ошибке. 
SRC / Products.jsx
 /* Import statements have been left out for code brevity */ const Products = ({ match }) => { const productsData = [ { id: 1, name: 'NIKE Liteforce Blue Sneakers', description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin molestie.', status: 'Available' }, //Rest of the data has been left out for code brevity ]; /* Create an array of `<li>` items for each product var linkList = productsData.map( (product) => { return( <li> <Link to={`${match.url}/${product.id}`}> {product.name} </Link> </li> ) }) return( <div> <div> <div> <h3> Products</h3> <ul> {linkList} </ul> </div> </div> <Route path={`${match.url}/:productId`} render={ (props) => <Product data= {productsData} {...props} />}/> <Route exact path={match.url} render={() => ( <div>Please select a product.</div> )} /> </div> ) } 
  Сначала мы создали список <Links> с использованием productsData.id и сохранили его в linkList .  Маршрут принимает параметр в строке пути, который соответствует идентификатору продукта. 
 <Route path={`${match.url}/:productId`} render={ (props) => <Product data= {productsData} {...props} />}/> 
  Возможно, вы ожидали component = { Product } вместо встроенной функции рендеринга.  Проблема в том, что нам нужно передать productsData компоненту Product вместе со всеми существующими реквизитами.  Хотя есть и другие способы сделать это, я считаю этот метод наиболее простым.  {...props} props {...props} использует синтаксис расширения ES6 для передачи всего объекта props компоненту. 
Вот код для компонента продукта.
SRC / Product.jsx
 /* Import statements have been left out for code brevity */ const Product = ({match,data}) => { var product= data.find(p => p.id == match.params.productId); var productData; if(product) productData = <div> <h3> {product.name} </h3> <p>{product.description}</p> <hr/> <h4>{product.status}</h4> </div>; else productData = <h2> Sorry. Product doesnt exist </h2>; return ( <div> <div> {productData} </div> </div> ) } 
  Метод find используется для поиска в массиве объекта со свойством id, равным match.params.productId .  Если продукт существует, отображается productData .  Если нет, то выводится сообщение «Товар не существует». 
Защита маршрутов
  В заключительной демонстрации мы обсудим методы защиты маршрутов.  Так что, если кто-то попытается получить доступ к /admin , он должен будет сначала войти в систему.  Однако есть некоторые вещи, которые мы должны охватить, прежде чем сможем защитить маршруты. 
Перенаправление
  Как и перенаправления на стороне сервера, <Redirect> заменит текущее местоположение в стеке истории новым местоположением.  Новое местоположение указывается to поддержки.  Вот как мы будем использовать <Redirect> : 
 <Redirect to={{pathname: '/login', state: {from: props.location}}} 
  Таким образом, если кто-то попытается получить доступ к /admin во время выхода из системы, он будет перенаправлен на маршрут /login .  Информация о текущем местоположении передается через состояние, поэтому, если аутентификация прошла успешно, пользователь может быть перенаправлен обратно в исходное местоположение.  Внутри дочернего компонента вы можете получить доступ к этой информации по адресу this.props.location.state . 
Пользовательские маршруты
Пользовательский маршрут — это модное слово для маршрута, вложенного в компонент. Если нам нужно принять решение о том, следует ли отображать маршрут или нет, написание нестандартного маршрута — это путь. Вот пользовательский маршрут, объявленный среди других маршрутов.
SRC / App.js
 /* Add the PrivateRoute component to the existing Routes */ <Switch> <Route exact path="/" component={Home} data={data}/> <Route path="/category" component={Category}/> <Route path="/login" component={Login}/> <PrivateRoute authed={fakeAuth.isAuthenticated} path='/products' component = {Products} /> </Switch> 
  fakeAuth.isAuthenticated возвращает true, если пользователь вошел в систему, и false в противном случае. 
Вот определение для PrivateRoute:
SRC / App.js
 /* PrivateRoute component definition */ const PrivateRoute = ({component: Component, authed, ...rest}) => { return ( <Route {...rest} render={(props) => authed === true ? <Component {...props} /> : <Redirect to={{pathname: '/login', state: {from: props.location}}} />} /> ) } 
  Маршрут отображает компонент Admin, если пользователь вошел в систему. В противном случае пользователь перенаправляется в /login .  Преимущество этого подхода в том, что он явно более декларативный, а PrivateRoute можно использовать повторно. 
Наконец, вот код для компонента Login:
SRC / Login.jsx
 import React from 'react'; import { Redirect } from 'react-router-dom'; class Login extends React.Component { constructor() { super(); this.state = { redirectToReferrer: false } // binding 'this' this.login = this.login.bind(this); } login() { fakeAuth.authenticate(() => { this.setState({ redirectToReferrer: true }) }) } render() { const { from } = this.props.location.state || { from: { pathname: '/' } } const { redirectToReferrer } = this.state; if (redirectToReferrer) { return ( <Redirect to={from} /> ) } return ( <div> <p>You must log in to view the page at {from.pathname}</p> <button onClick={this.login}>Log in</button> </div> ) } } /* A fake authentication function */ export const fakeAuth = { isAuthenticated: false, authenticate(cb) { this.isAuthenticated = true setTimeout(cb, 100) }, } 
Строка ниже демонстрирует разрушение объекта , которое является частью спецификации ES6.
 const { from } = this.props.location.state || { from: { pathname: '/' } } 
Давайте соединим кусочки головоломки вместе, ладно? Вот последняя демонстрация приложения, которое мы создали с использованием маршрутизатора React:
Демонстрация 4: Защита маршрутов
Резюме
Как вы видели в этой статье, React Router — мощная библиотека, которая дополняет React для создания лучших декларативных маршрутов. В отличие от предыдущих версий React Router, в v4 все «просто компоненты». Более того, новый шаблон дизайна идеально вписывается в стиль действий React.
В этом уроке мы узнали:
- как настроить и установить React Router
 -   основы маршрутизации и некоторые важные компоненты, такие как 
<Router>,<Route>и<Link> - как создать минимальный роутер для навигации и вложенных маршрутов
 - как строить динамические маршруты с параметрами пути
 
Наконец, мы изучили некоторые продвинутые методы маршрутизации для создания финальной демонстрации для защищенных маршрутов.