С помощью этого туториала вы сможете лучше понять, как работает роутинг в реакт приложениях. Так же будет рассмотрена асинхронная загрузка данных.
За основу, будет взят React-router 4 и его компоненты Router и Link с минимальным кодом.
Нам предстоит сделать книжную полку
Этот материал был разобран на вебинаре, поэтому есть видео-версия происходящего.
Исходный код, само собой.
Для начала нужно развернуть приложение с помощью create-react-app
Если вы используете yarn
, то можете писать yarn start
Роутинг — это соответствие между контентом на экране и URL-адресом в адресной строке браузера.
Сделать приложение, в котором будет три ссылки:
Следовательно, на языке реакта нам нужно сделать три роута (_Route_)
Прикинем как будет выглядеть компонент App:
src/components/App.js
Ничего особенного. Но есть недостающие компоненты Link
и Route
Компонент Link достаточно прост:
src/components/Router.js
В методе render
возвращается обычная ссылка, а вот click
по ссылке выглядит интереснее.
В функциях historyPush
/ historyReplace
используется стандартный объект браузера — window.history. Вновь никакой магии в мире JavaScript, увы но React-Router делает то же самое: изменяет историю с помощью нативных методов pushState и replaceState. Эти методы, «добавляют» (push) или «заменяют» (replace) записи в объекте history. Таким образом браузер изменяет URL-адрес без перезагрузки страницы.
В коде нашего приложения, роуты выглядят так:
Route — это обычный React-компонент, он принимает следующие свойства:
Первый роут (на самом деле, по английски произносится «рут») — рисует компонент Home
, только если URL-адрес равен http://localhost:3000/ (строго!)
Второй роут (/_epic_) рисует компонент Epic
, причем будет его рисовать, даже если адрес будет http://localhost:3000/epic/bla-bla/bla-bla-bla-bla, потому что не указан атрибут exact
.
Третий роут, примечателен тем, что он не содержит свойства component
, а содержит сразу функцию, которая должна отработать, если в URL-адресе, появилось /about
.
Супер! Требования для комопонета поставлены, приступим к его реализации.
src/components/Router.js
Если мы откроем наше приложение, то увидим следующую картину: все роуты будут отрисованы, при этом — клики по ссылкам будут изменять URL-адрес корректно, однако, наше приложение никак не будет реагировать на это. Зато, будет реагировать браузер! Если вы начнете кликать по ссылкам, то увидите, что кнопка «Назад» стала активной. Раз уж мы пользуемся «нативными» возможностями, то, разумеется, браузер сразу же начинает «понимать» нас.
Нам необходимо создать переменную-флаг, которая будет сигнализировать о том, нужно ли отрисовывать компонент. Здесь нет никаких затруднений, так как если мы из метода render
что-то возвращаем с помощью ключевого слова return
— то код ниже строки в которой указан return
не выполняется. Исключение — когда мы из return
возвращаем что-либо в круглых скобках, в таком случае, JavaScript выполнит все, что указано в скобках и покинет функцию.
У React-router есть свойство match
, в котором содержится полезная информация. Сделаем подобное.
Для начала создадим вспомогательную функцию.
src/components/Router.js
Теперь, воспользовавшись этой функцией, мы можем убить сразу двух зайцев:
render
методе Route
— что рисовать, а что нет.src/components/Router.js
Ок, сейчас у нас в приложении не отрисовываются сразу все компоненты. Но проблемы остались — клики по ссылкам, а так же кнопки назад/вперед — по прежнему не «перерисовывают» контент. Спасает только перезагрузка страницы, после которой отрисовывается то, что нужно.
[1] — фунция matchPath использует стандартное свойство window.location.pathname.
Как и у всего в мире JS, у window.history
есть своя событийная модель. Нас интересует событие popstate.
Поскольку, мы занимаемся разработкой «библиотеки», то нам и все карты в руки. Мы уже использовали React.createElement, пора задействовать и forceUpdate.
forceUpdate
— позволяет нам жестко указать реакту — перерисуй компонент (т.е. вызови метод render
).
src/components/Router.js
Попробуйте!
Клики по ссылкам по-прежнему только лишь изменяют URL-адрес, но кнопки «назад/вперед» — работаю корректно. Почему так? Потому что, событие popstate
срабатывает только при нажатии на эти кнопки. К моему большому сожалению, pushState
/ replaceState
не «генерируют» события popstate
. Я даже позволю себе смайлик ;(
Выход есть:
onClick
в компоненте Link
) идти по «зарегистрированным компонентам» и вызывать на каждом forceUpdate
.Вуаля, теперь работает: при клике по ссылке, при переходах с помощью назад/вперед — контент меняется без перезагрузки страницы.
Чтобы красиво закончить наш «роутер», перепишем компонент Epic таким образом, чтобы он отображал ссылки на серии о Гарри Поттере, Властелине Колец и Ганнибале. Хорошая компания!
Из компонента App, нужно удалить определение компонента Epic и добавить import.
src/components/Epic.js
Обратите внимание, что мы используем «полезности» из свойства match, которое заблаговременно передаем.
Итого, path всех роутов будет /epic/название_серии
Следовательно, если мы захотим изменить epic на что-то другое, нам не нужно будет «бегать автозаменой» по всем файлам. Удобно.
Роутер готов, можем приступать ко второй части — загрузка данных.
Какова задача? Нужно при клике на ссылку — рисовать компонент Book
и в нем запрашивать данные.
Чтобы компонент запрашивал данные, нужное где-то данные разместить. Для удобства будем использовать сервис — MockAPI, который позволяет имитировать рабочий сервер: то есть, по GET запросу, мы будем получать данные по книгам.
Сервис MockAPI настолько прост, что после регистрации, нужно лишь указать имя проекта и желаемые данные. Обратите внимание, что сервис поддерживает GET/PUT/DELETE с параметром id. Нам это пока что не пригодится.
Для нашего приложения, мы можем выбрать любую вложенность данных. Для простоты будем использовать несколько ресурсов: potter, lord, hannibal. Внутри которых будут похожие данные.
Пример данных для ресурса potter:
Обычный JSON: имя (_name_), URL адрес для картинки (_imgUrl_), описание (_description_).
Для получения данных асинхронно, будем использовать нативный fetch. А так же ознакомимся с async/await.
Процесс работы с асинхронными данными не отличается от обычной работы с данными, которые изменяются. Уже уловили откуда дует ветер? Если данные изменяются, значит место им в state.
Прикинем, что нам нужно хранить в state компонента, который запрашивает данные (по другому, такой компонент еще называют «контейнером«):
isLoading: true/false
)books
и его «класть» в state. Значит, начальное состояние данных в state — пустой массив.src/components/Book.js
Листинг кода содержит подробные комментарии. Думаю для тех, кто знаком с работой функций map и promise, такой код не покажется сложным.
Так же, в коде я использую самописную функцию-хелпер (_помощницу_) httpGet
, так как предполагается, что в приложении много где будет нужно запрашивать данные с сервера, и чтобы не дублировать код, он вынесен в отдельную функцию.
src/helpers/network.js
Обратите внимание, что у нас отсутствует обработка ошибок, и если запрос завершится неудачно — все приложение «умрет». Мы исправим этот момент в заключительном разделе.
А пока создадим компонент BookInfo
, чтобы посмотреть на результат в действии.
src/components/BookInfo.js
Самый обычный компонент, разве что, вместо div
я использовал возможности Реакта 16й версии — React.Fragment.
Сейчас при запросе книг про Ганнибала Лектера, MockAPI возвращает 404, так как я не указал никаких JSON-данных для этого ресурса.
Во-первых, в рамках JS мы должны обрабатывать такую ошибку с помощью блока try..catch.
src/helpers/network.js
Во-вторых, мы должны обновить компонент Book
:
error: false
error
на true
если произошла ошибка, и isLoading
на false
, так как данные уже не загружаютсяisLoading
на true
, стереть ошибку (error: false
) и выполнить запрос.src/components/Book.js
Результат:
Сегодня будем использовать parcel и IntelliJ IDEA Community Edition. Все инструменты бесплатные. Инициализация elm проекта…
На данном вебинаре мы знакомились с языком Elm проводя параллели между Elm и Redux, поэтому…
Richard Feldman рассказывает как масштабировать Elm приложение без боли. Показаны техники: extended records, подход narrow…
В данной заметке вы найдете конспект видео по Elm, которые я посмотрел в ноябре 2019.…
Итоги года 2019 // Max Frontend Покажи мне свой гитхаб, и я скажу работал ли…
Почему стоит изучать Elm? Потому что это интересный вызов, редкие (но вкусные) вакансии и хороший…
View Comments
Добрый день. Зачем создавать компоненты Router и Link если она уже есть в react-router-dom?
Добрый день, это была тема вебинара, на котором мы пытались создать "свой" роутер. То есть, изучали как это может быть "изнутри".
жесть, автор постарался)