React урок 5 Маршрутизация

URL является важной частью веб-приложений. Изначально задумывался как простой указатель на файл, который лежит на сервере, но с появлением веб-приложений лучше думать о нем, как о текущем состоянии приложения. Глядя на него пользователь может понять где он в настоящее время находиться, а также может скопировать его для последующего использования или кому-то передать.

Реализация маршрутизации «наивный» путь

Чтобы понять, как работает маршрутизация, давайте рассмотрим пример. Мы создадим приложение, которое будет использовать GitHub API, получения списка репозиториев. Помимо этого добавим домашнюю страницу и «О проекте».  Давайте начнем с главного компонента App:

Код довольно прост. В конструкторе компонента, мы получаем текущее значение хеш URL-адрес и присваиваем состояние маршрута.  Затем, при создании компонента, добавляем обработчик события на изменение URL, в нем обновляется текущее состояние нашего компонента. Теперь нам надо обновить метод render:

Тут все тоже достаточно просто, проверяется текущее состояние route и в зависимости от него Child присваивается определенный компонент. Все дочерние компоненты которые представляют собой внутренние страницы приложения просты и имеют одинаковую структуру:

Добавим немного красоты:

Несмотря на то, что это работает, в данном подходе есть по крайней мере две проблемы:

  • В данном примере, содержимое URL заняло центральное место: вместо того чтобы автоматически обновление URL и состояние приложения;
  • Код маршрутизации может расти в геометрической прогрессии при сложных нетривиальных сценариях. Представим себе, например, что внутри страницы Repos может быть список репозиториев со ссылкой внутрь, что-то вроде /repos/repo_id.

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

React Router

React Router является наиболее популярным решением для добавления маршрутизации React приложению. Он помогает держать в синхронизации UI c URL с помощью компонентов, связанных с маршрутами ( на любом уровне вложенности). При изменении URL адреса React Router автоматически монтирует и демонтирует нужные компоненты. Поскольку React Router является внешней библиотекой, то она должна быть установлена с npm:

npm install —save react-router

React Router предоставляет три компонента для начала работы:

  • Router и Route: Используются для декларативного описания карты маршрутов приложения;
  • Link: Используется для создания ссылок с заданным href. Это не единственный способ навигации проекта, но все же основной.

Давайте изменим наш пример, вооружившись React Router:

Здесь мы импортировали React Router и обновили метод render добавив туда Link, а также избавились от конструктора и componentDidMount. Теперь давайте обновим нижний метод render:

Index Route

Если попробовать запустить, то мы увидим, что все работает как раньше, но если зайти «/» то не увидим результат компонента «Home». Первый что приходит на ум это использовать Route, но какой путь указать?

Вместо этого, можно использовать IndexRoute. Надо просто импортировать его и использовать его для настройки маршрут индекса:

Маршруты с параметрами

Теперь у нас есть реализация на одном уровне нашим первым «наивным» примером маршрутизации, давайте расширим его извлекая данные из GitHub.

Теперь есть попробовать зайти на страницу Repos мы увидим список репозиториев. Далее, нам неплохо бы добавить для каждого элемента списка репозиториев ссылку на подробности имеющую примерно такой вид /repos/details/repo_name. Создадим для этих целей новый компонент RepoDetails, но перед этим давайте обновим список добавим Link:

При создании RepoDetails, есть две особенности:

  • React Router будет передавать параметр URL через реквизит params значения параметров URL в нашем случае repo_name. Мы может использовать его для получения деталей о репозитории.
  • Во вторых мы будем обновлять данные о репозитории не только при создании компонента (componentDidMount), но и при изменении реквизитов (componentWillReceiveProps).

Давай приступим:

Для завершения нам надо RepoDerails добавить в App.js:

Здесь мы импортировали RepoDetails и добавили в Route Repos новый вложенный Route c шаблоном url details/:repo_name и компонентом RepoDetails.

repo-router

Активная ссылка

Компонент Link имеет дополнительный атрибут activeClassName. Когда он установлен и текущий адрес URL совпадает с ссылкой, ссылке присваивается указанный CSS класс. Давайте добавим этот атрибут к нашим ссылкам:

Передача реквизитов

Есть небольшая проблема: мы делаем не нужный запрос GitHub API при переходе по ссылке к деталям репозиториев, потому как при https://api.github.com/users/pro-react/repos уже передаются нужные нам данные. Но это поправимо мы можем передать все данные о репозиториях вниз в качестве реквизита для отображения компонента repoDetails.

Есть два способа передачи реквизитов в React Router: указав реквизит при определении Route или путем инъекции реквизита.

Реквизиты Route

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

Давайте попробуем попробуем, добавим title к About:

А теперь давайте используем внутри About:

Клонирование и Инъекция реквизитов

Этот подход хорош для динамических реквизитов. Внутри Repo, вместо того, чтобы просто рендерить this.props.childre, можно клонировать его с передачей дополнительных реквизитов, в нашем случае это список репозиториев:

Теперь давайте поправим RepoDetails:

Изменение вложенного URL

/repos/details/:repo_name кажется немного длинным, не плохо было бы иметь что-то типа /repos/:repo_name.  Чтобы так сделать надо указать абсолютный путь в path компонента Route:

Еще надо поправить ссылку в Repos:

Изменение маршрутов программным способом

Компонент Link клевая штука, но иногда может потребоваться управление текущим положением программно. Например вернуться по какому-то событию назад или перенаправить пользователя на другой маршрут. Для этого React Router внедряет объект history во все компоненты указанные в Router. Объект history отвечает за управление историей браузера и обеспечивает методы навигации:

pushState Основной метод добавления в историю перехода но новый URL. При желании можно пропустить первый аргумент:

history.pushState(null, ‘/users/123’)
history.pushState({showGrades: true}, ‘/users/123’)

replaceState Имеет тот же синтаксис что и pushState, он заменяем текущий URL на новый. Это аналогично переадресации, так как он заменяет URL, не влияя на длину истории
goBack Возвращаемся назад по истории браузера
goForward Переходим вперед по истории браузера
Go Переход на n шагов в истории, если n положительно то вперед, если нет то назад
createHref Создает ссылку, используя настройки маршрутов

Чтобы разобраться, как это работает, давайте создадим новый маршрут Server Error. И если в компоненте Repo при загрузке списка репозиториев произойдет ошибка, то перенаправим на наш новый маршрут. Начнем с создания нового компонента ServerError:

А теперь давайте объявим его в App.js:

Теперь давайте перейдем к Repos:

Если отключить интернет при переходе Repo мы увидим ошибку:

error_router

Histories

React Router построен на основе библиотеки History. Ее целью является абстракция над управление URL и историей в различных браузерах и других платформ. History по умолчанию реагирует на маршруты использующие хеш (#) часть URL, например /#/path. И это хорошо работает для старых браузеров IE 8 9 и не требует ни какой конфигурации сервера. Если вашему приложению не нужно работать в старом браузере и вы можете настроить свой сервер, идеальный подход в таком случае будет использование browserHistory, которая создает реальные URL — адреса, например example.com/path.

Тут мы импортировали и указали что хотим использовать browserHistory.

Канбан доска: Маршрутизация

До сих пор наше приложение было очень эфективным в качестве упражнения, но без полезно само по себе, потому что мы не можем ни редактировать карточки не создавать их. Давайте реализуем эти две функции с помощью маршрутов. URL /new будет показывать окно создания новой карточки, а /edit/:card_id будет открывать окно редактирования. Для этого мы создадим компоненты NewCard и EditCard, а так как оба компонента разделяют множество общих характеристик (набор полей), мы создадим так же СardForm. Давайте разберем план действий:

  • Начинаем снизу в верх, с создания CardForm
  • Затем создадим Newcard и EditCard
  • Добавляем новые маршруты в App.js
  • В KanbanBoardContainer добавим методы редактирования и создания карточек. И передадим эти методы в Newcard и EditCard.

Но сначала нам надо установить React Router:

npm install —save react-router

Компонент CardForm

CardForm будет содержать форму, она будет выглядит как модальное окно:

conatct_form

Компонент CardForm будет чистым, без состояния. Оба компонента Newcard и EditCard должны будут предоставить следующие реквизиты:

  • Объект, содержащий значение для отображения на карточке.
  • Заголовок кнопки подтверждения.
  • Функция обработки формы.
  • Функция обработки закрытия окна.

Код:

Теперь давайте добавим немного красоты CSS:

Компоненты NewCard и EditCard

Давайте перейдем к компонентам NewCard и EditCard.

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

Давайте пока пропустим KanbanBoardContainer и перейдем к App.js. Это поможет лучше понять как модифицировать KanbanBoardContainer. Мы создадим три маршрута: ведущая к доске, созданию новой карточки и редактированию карточки.

KanbanBoardContainer обратные вызовы

Давайте добавим два новых метода создания и удаления карточек.

И, наконец, нам надо обновить метод render.

Рендеринг Card Forms

Чтобы появилось окно, осталось отредактировать рендеринг KanbanBoard:

Последние штрихи: переход

Если в ручную ввести /new, то мы увидим нашу форму, но это не дело что бы так вызывать ее, давайте добавим кнопку:

add_button

А теперь кнопка редактирования:

Итоги

Сегодня мы разобрали с вами маршрутизацию в React приложениях. Видели какие могу быть сложности с вложенными путями. Рассмотрели замечательную библиотеку React Route для построение маршрутизации в наших приложения. Узнали как можно управлять программно перемещением по маршрутам. В итоге наше приложение  Канбан доска становиться все более крупным и начинают чувствоваться «боли» роста. В следующий раз мы разберем, как строить приложения с помощью Flux, что поможет нам лучше организовать свой проект.