Компоненты и коллекции
1. Веб-приложения
В современной веб-разработке изменились не только техники позволяющие веб-сайтам выглядеть лучше, загружаться быстрее и быть приятнее в использовании. В первую очередь изменились фундаментальные вещи - то, как мы проектируем и создаем веб-приложения.
Возьмем произвольный веб-сайт, например для работы с коллекцией рецептов, расписанием тренировок и т. п. Всегда есть набор страниц: домашняя, профиль, страница коллекции и страница одного элемента коллекции.
1.1. Multiple-page Application
Несколько лет назад мы бы использовали подход включающий несколько отдельных HTML-страниц.
Архитектура клиент-сервер
Вся логика живет на сервере
На каждый запрос сервер отсылает готовый HTML-документ
Перезагрузка страницы при каждом запросе
Плохая интерактивность
Отличное SEO
1.2. Single-page Application
Современный подход - сайт, на котором пользователь никогда не переходит на другие HTML-страницы. Интерфейс, вместо запроса HTML-документов с сервера, перерисовывается на клиенте, на одной и той же странице, без перезагрузки.
Архитектура клиент-сервер
При загрузке сайта сервер всегда отдает стартовую HTML-страницу
index.html
Каждый последующий запрос на сервер получает только данные в JSON-формате
Обновление интерфейса происходит динамически на клиенте
Загрузка первой страницы может быть довольно медленной (лечится)
Логика не связанная с безопасностью живет на клиенте
Слабое SEO (лечится)
Сложность кода и его поддержки масштабируется с кол-вом функционала приложения
2. React
Библиотека для создания элементов интефрейса. В React нет встроенной маршрутизации, HTTP-модуля и т. п. Тем не менее есть богатая экосистема, которая позволит решить любую задачу.
При создании приложения с использованием React, разработчик не взаимодействует с DOM-деревом напрямую. Его задача описать интерфейс с помощью компонентов (шаблон) и управлять изменением данных (модель). React, при изменении данных модели, сам обновит интерфейс по шаблону.
3. Browser DOM и Virtual DOM
Browser DOM - древовидное представление HTML-документа, где каждый элемент документа представлен в виде DOM-узла. Хранится в браузере и напрямую связан с тем что мы видим на странице.
При каждом изменении DOM, браузер выполняет несколько трудоемких операций. Частые операции обновления такого дерева негативно влияют на производительность и отзывчивость интерфейса. Поэтому он медленный и обновлять его необходимо эффективно.
Virtual DOM - абстракция, легковесная копия реального DOM-дерева в виде JSON-документа.
Существует только в памяти и не рендерится в браузере
Не зависит от внутренней имплементации браузера
Использует лучшие практики обновления реального DOM
Собирает обновления в группы для оптимизации рендера (batching)
4. Инструменты
Для создания React-приложения необходимы Node.js
, Webpack
, Babel
, React
и DevTools
. Можно написать свою Webpack-сборку или взять любую хорошую с GitHub.
4.1. Create React App
Для обучения и маленьких/средних проектов рекомендуется использовать утилиту от авторов React.
Абстрагирует всю конфигурацию, позволяя сосредоточиться на написании кода
Включает необходимые инструменты:
Webpack
,Babel
,ESLint
и т. п.Расширяется дополнительными пакетами из экосистемы React
Имеет функцию извлечения, которая удаляет абстракцию и открывает конфигурацию
npx — инструмент, предназначенный для того, чтобы помочь стандартизировать использование npm-пакетов. Поставляется с npm версии 5.2.0
и выше. npm
упрощает установку и управление зависимостями, размещенными в реестре, a npx
упрощает использование CLI-утилит и других исполняемых файлов без необходимости их установки в систему или проект.
4.2. React DevTools
В инструментах разработчика можно посмотреть на дерево компонентов, их состояние и пропсы. Профайлер полезен при оптимизации приложения.
5.React-элементы
React-элементы - это самые маленькие строительные блоки React, элементы Virtual DOM. Элементы это обычные JS-объекты, поэтому создавать их очень быстро.
Функция React.createElement()
это самый главный метод предоставляемый React API. Подобно document.createElement()
для DOM, React.createElement()
это функция для создания React-элементов. Возвращает объект, элемент Virtual DOM.
type
- имя встроенного React-элемента который в Virtual DOM соответсвует будущему HTML-тегу.props
- объект содержащий HTML-атрибуты и кастомные свойства. Может бытьnull
или пустой объект, если передавать ничего не нужно.children
- произвольное количество аргументов после второго это дети создаваемого элемента. Так создается дерево элементов.
Создадим элемент с детьми, карточку продукта.
5.1. Рендер элемента в DOM-дерево
Для того чтобы отрендерить элемент, в пакете react-dom
есть метод ReactDOM.render()
.
Первым аргументом принимает ссылку на React-элемент или компонент (что рендерить)
Вторым, ссылку на уже существующий DOM-элемент (куда рендерить)
React использует модель отношений предок - потомок
, поэтому достаточно использовать только один вызов ReactDOM.render()
в приложении. Рендер самого верхнего элемента в иерархии повлечет за собой рендер всего поддерева.
6. JavaScript Syntax Extension (JSX)
Код в предыдущем разделе понятен браузеру. Но так описывать разметку интерфейса неудобно, нам привычен HTML. Для этого был создан JSX
.
Позволяет использовать XML-образный синтаксис прямо в JavaScript
Упрощает код, делает его декларативным и читабельным
Описывает объекты - элементы Virtual DOM
Это не HTML, Babel трансформирует JSX в вызовы
React.createElement()
В JSX можно использовать весь потенциал JavaScript
JSX не обязателен, но давайте сравним следующий код.
JSX
значительно чище и читабельнее. Используя JSX
, компоненты становятся похожи на HTML-шаблоны. Перепишем карточку продукта.
JSX преобразовывается в вызовы
React.createElement()
, поэтому пакет React должен быть в области видимости модуля.Можно использовать практически любое валидное JavaScript-выражение, оборачивая его в фигурные скобки.
Значения атрибутов указываются через двойные кавычки, если это обычная строка, и через фигурные скобки, если значение вычисляется, либо тип отличается от строки.
Все атрибуты React-элементов именуются в
camelCase
нотации.JSX-теги могут быть родителями других JSX-тегов. Если тег пустой или самозакрывающийся, его обязательно необходимо закрыть используя
/>
.
6.1. Правило общего родителя
Разберем следующий код с не валидной JSX-разметкой.
Перепишем код используя React.createElement()
.
Невалидное выражение справа от оператора присваивания, потому что само по себе присваиваемое выражение не имеет смысла. Выражение это одно значение, результат неких вычислений, отсюда и правило общего родителя.
В JSX это выглядит так.
Если в разметке лишний тег-обертка не нужен, используются Фрагменты
, похожие на DocumentFragment
. Этот встроенный компонент при рендере растворяется, подставляя свое содержимое.
Синтаксис фрагментов можно сократить и не добавлять импорт Fragment
. Babel сделает все необходимые трансформации, заменив пустые JSX-теги на React.Fragment
.
6.2 Дополнительные материалы
7. Компоненты
Компоненты - основные строительные блоки React-приложений, при помощи которых интерфейс делится разделить на независимые части.
Разработчик создает небольшие компоненты, которые можно объединять, чтобы сформировать более крупные или использовать их как самостоятельные элементы интерфейса. Самое главное в этой концепции то, что и большие, и маленькие компоненты можно использовать повторно и в текущем и в новом проекте.
React-приложение можно представить как дерево компонентов. На верхнем уровне стоит корневой компонент, в котором вложено произвольное количество других компонентов. Каждый компонент должен вернуть JSX-разметку, тем самым указывая какой HTML мы хотим отрендерить в DOM.
7.1. Компоненты-функции
В простейшей форме компонент это JavaScript-функция с очень простым контрактом: функция получает объект свойств который называется props
и возвращает дерево React-элементов.
Имя компонента обязательно должно начинаться с заглавной буквы. Названия компонентов с маленькой буквы зарезервированы для HTML-элементов. Если вы попробуете назвать компонент card
, а не Card
, при рендере, React проигнорирует его и отрендерит тег <card></card>
.
Компоненты-функции составляют большую часть React-приложения.
Меньше boilerplate-кода
Легче воспринимать
Легче тестировать
Нет контекста (this)
Сделаем карточку продукта компонентом-функцией.
8. Свойства компонента (props)
Свойства (пропсы) это одна из основных концепций React. Компоненты принимают произвольные свойства и возвращают React-элементы, описывающие что должно отрендерится в DOM.
Пропсы используются для передачи данных от родителя к ребенку.
Пропсы передаются только вниз по дереву от родительского компонента.
При изменении пропсов React ре-рендерит компонент и, возможно, обновляет DOM.
Пропсы доступны только для чтения, изменить их в ребенке нельзя.
Пропсом может быть текст кнопки, картинка, url, любые данные для компонента. Пропсы могут быть строками или результатом JS-выражения. Если передано только имя пропса - это буль, по умолчанию true
.
Компонент <Product>
объявляет параметр props
, это всегда будет объект содержащий все переданные пропсы.
Добавим компоненту <Products>
несколько других свойств.
Сразу будем использовать простой паттерн при работе с props
. Так как props
это объект, мы можем деструктуризировать его в подписи функции. Это сделает код чище и читабельнее.
В результате мы создали настраиваемый компонент который можно использовать для отображения товара. Мы передаем ему данные как пропсы, а в ответ получаем дерево React-элементов с подставленными значениями.
8.1. Свойство props.children
Концепция дочерних элементов позволяет очень просто делать композицию компонентов. В виде детей можно передавать компоненты, как встроенные так и кастомные. Это очень удобно при работе со сложными составными компонентами.
Свойство
children
автоматически доступно в каждом компоненте, его содержимым является то, что стоит между открывающим и закрывающим JSX-тегом.В функциональных компонентах обращаемся как
props.children
.Значением
props.children
может быть практически что угодно.
К примеру у нас есть компонент профиля <Profile>
и вспомогательный ui компонент <Panel>
, в который мы можем помещать произвольный контент.
В противном случае нам бы пришлось пробросить пропы для <Profile>
сквозь <Panel>
, что более тесно связывает компоненты и усложняет повторное использование.
8.2. Свойство defaultProps
Что если компонент ожидает какое-то значение, а его не передали? - при обращении к свойству объекта props
, получим undefined
.
Для того чтобы указать значения свойств по умолчанию, у компонентов есть статическое свойство defaultProps
, в котором можно указать объект с дефолтными значениями пропов (не обязательно всех). Этот объект будет слит с пришедшим объектом props
.
8.3. Свойство propTypes
Проверка типов получаемых пропсов позволит отловить много ошибок. Это экономит время на дебаг, помогает при невнимательности и спасает при росте приложения. В будущем будет необходимо выделить время и познакомиться с Flow или TypeScript, а для старта хватит небольшой библиотеки.
Используем prop-types
и опишем пропсы компонента Product
.
Сначала применяются значения по умолчанию, заданные в defaultProps
. После запускается проверка типов с помощью propTypes
. Так что проверка типов распространяется и на значения по умолчанию.
9. Рендер по условию
Для рендера разметки по условию используются операторы ветвлений и условий. Условия можно проверять перед возвратом разметки, или прямо в JSX.
9.1. if с помощью логического оператора &&
Читается как: если условие приводится к true
, то рендерим разметку.
9.2. if...else с помощью тернарного оператора
Читается как: если условие приводится к true
, рендерим разметку после ?
, в противном случае рендерим разметку после :
.
Последний пример можно записать по другому, результат будет одинаковый.
Пусть в компоненте продукта еще есть его доступное количество.
9.3. Дополинтельные материалы
10. Коллекции
Для того чтобы отрендерить коллекцию однотипных элементов, используется метод Array.prototype.map()
, callback-функция которого, для каждого элемента коллекции, возвращает JSX-разметку. Таким образом получаем массив React-элементов который можно рендерить.
10.1. Ключи
При выполнении кода из примера выше, всплывет предупреждение о том, что для элементов списка требуется ключ. React не может отличить элементы в коллекции, таким образом, перерисовывая всю коллекцию целиком при любых изменениях.
Ключ (key) — это специальный строковый проп, который нужно задать при создании элементов коллекции.
Элементы внутри коллекции должны быть обеспечены ключами, чтобы иметь стабильную идентичность. React использует ключи, чтобы определить, какие из элементов в коллекции необходимо создать и отрендерить заново, а не использовать элементы из предыдущего рендера. Так мы избегаем пересоздания всех элементов коллекции каждый раз, когда что-то меняется.
Ключи должны быть:
Уникальные - ключ элемента должен быть уникальным только среди его соседей. Нет смысла в глобально уникальных ключах.
Стабильные - ключ элемента не должен меняться со временем, изменением порядка элементов или после обновления страницы.
Лучший способ задать ключ — использовать статическую строку, которая однозначно идентифицирует элемент списка среди остальных. Чаще всего в качестве ключей используются идентификаторы объектов созданных базой данных - постоянное, неизменное значение.
10.2. Дополнительные материалы
Last updated
Was this helpful?