5.6 Маршрутизация


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




5.6.1 Для чего нужна маршрутизация?


В любом реальном веб-приложении нужны маршруты, и приложение React не исключение. Пользователь должен видеть, где он находится в приложении в любой момент времени. А видит он свое текущее местоположение в адресной строке браузера. Следовательно приложение должно уметь сопоставлять определённый URL с соответствующей ему страницей. То есть, если мы введём в адресную строку например https://health-imperium/appointmens, то приложение должно направить нас на страницу списка приёмов, но не на какую-либо другую.

Также должна работать история. То есть когда пользователь кликает на стрелку "Назад" в браузере, приложение должно направить нас на предыдущую страницу.

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

На данный момент есть несколько популярных библиотек для маршрутизации: react-router, router5, aviator и пр. Полный список можно посмотреть здесь. Мы будем использовать react-router. Хотя в вашем проекте может быть любая другая - все зависит от бизнес-требований.



5.6.2 Библиотека react-router


Эта библиотека популярна, довольна проста в использовании и обладает хорошей документацией. Она предоставляет такие возможности как:

  • Навигация по клику (компонент <Link>)

  • Перенаправление (компонент <Redirect>)

  • Маршрутизация (компонент Route)

  • История (свойство history)

Это лишь малая часть того, что умеет библиотека. В нашем приложении мы будем использовать самые базовые вещи, чтобы у вас было общее представление о сути маршрутизации, о том как её подключать и использовать.



5.6.3 Добавление react-router в приложение


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



Теперь подключим в наше приложение эту возможность, обновив файл index.js:


Код
    
  import React from 'react'
  import ReactDOM from 'react-dom'

  import { Router } from "react-router-dom"
  import {createBrowserHistory} from 'history'

  import './index.scss'

  import App from './App'

  // создаём кастомную историю
  const history = createBrowserHistory()

  ReactDOM.render((
     <Router history={history}>
       <App/>
     </Router>
   ), document.getElementById('root')
  );
  

Мы создали объект кастомной истории и передали его как свойство в компонент <Router>. Этого можно и не делать. Достаточно использовать компонент <BrowserRouter> вместо <Router>, тогда будет использован API истории HTML5.

Далее необходимо подключить саму библиотеку react-router. Для этого нужно установить модуль react-router-dom.




Код
    
  import React, { Component } from 'react';

  import {
    Route,
    Switch,
    Redirect,
    withRouter
  } from "react-router-dom"

  import './App.scss';

  import Home from './components/Home/Home'
  import Appointments from './components/Appointments/Appointments'

  class App extends Component {
    render() {
      const { history } = this.props

      return (
        <div className="App">
          <Switch>
            <Route history={history} path='/home' component={Home} />
            <Route history={history} path='/appointments' component={Appointments} />
            <Redirect from='/' to='/home'/>
          </Switch>
        </div>
      );
    }
  }

  export default withRouter(App)
  

Как видно, у нас пока два маршрута: /home и /appointments. Введя в адресную строку один из них, приложение перенаправит нас на соответствующую страницу. Элемент <Redirect> здесь задаёт страницу по умолчанию. Отдельно стоит поговорить о withRouter. Это старший компонент, он же HOC. С помощью него вы можете получить доступ к свойствам объекта history и к объекту match, ближайшего <Route>. withRouter будет передавать в обёрнутый компонент обновленные свойства match, location, и history каждый раз, когда тот отрисовывается.

Наконец, нам осталось обновить компонент <Home>, добавив в него навигацию, с помощью компонента <Link>:


Код
    
  import React, { Component } from 'react'

  import { map } from 'underscore'
  import { Link } from "react-router-dom"

  import './Home.scss'

  import Header from '../Header/Header'

  import { ReactComponent as User } from '../../images/user.svg'
  import { ReactComponent as Star } from '../../images/star.svg'
  import { ReactComponent as Nurse } from '../../images/nurse.svg'
  import { ReactComponent as House } from '../../images/house.svg'
  import { ReactComponent as Clients } from '../../images/clients.svg'
  import { ReactComponent as Messages } from '../../images/messages.svg'
  import { ReactComponent as Broadcast } from '../../images/broadcast.svg'
  import { ReactComponent as Employees } from '../../images/employees.svg'
  import { ReactComponent as Appointment } from '../../images/appointment.svg'

  const TITLE = 'Домашняя'

  const SECTIONS = [
    { title: 'Приёмы', href: '/appointments', Icon: Appointment },
    { title: 'События', href: '/events', Icon: Star  },
    { title: 'Оповещения', href: '/notifications', Icon: Broadcast },
    { title: 'Сообщения', href: '/messages', Icon: Messages },
    { title: 'Клиенты', href: '/clients', Icon: Clients },
    { title: 'Сотрудники', href: '/employees', Icon: Employees }
  ]

  export default class Home extends Component {

    render () {
      return (
        <div className='Home'>
          <Header
                  title={TITLE}
                  userName='Иванов Иван Иванович'
                  className='Home-Header'
                  renderIcon={() => (
              <House className='Header-Icon'/>
            )}
          />
          <div className='Home-Body'>
            <div className='SectionNavigation'>
              {map(SECTIONS, ({ title, href, Icon }) => (
                // с помощью компонента Link будет осуществляться
                // навигация по разделам приложения
                <Link className='SectionNavigation-Item Section' to={href}>
                  <Icon className='Section-Icon'/>
                  <span className='Section-Title'>{title}</span>
                </Link>
              ))}
            </div>
          </div>
        </div>
      )
    }
  }
  

Все необходимые изменения сделаны, и теперь вы можете протестировать работу приложения, открыв его в отдельной вкладке:



Перейдя на новую вкладку вы увидите текущий путь в адресной строке:



Теперь попробуйте перейти на список приёмов и понаблюдайте за изменением текущего пути. Затем вернитесь назад, нажав на соответствующую кнопку в браузере. Сейчас вы видите маршрутизацию и историю в действии.