Конспект по основам React (библиотека JavaScript)

Занимаюсь сейчас активно изучением JavaScript и React. Gutenberg и FSE (Full Site Editing) в WordPress уверенно диктуют новый тренд в стэке используемых технологий. В этом посте буду публиковать интересные заметки, сделанные мной в процессе обучения. Это будет небольшой конспект по основам React.
Приготовления
- Основы JavaScript, которые необходимы для изучения React #1
- Основы JavaScript, которые необходимы для изучения React #2
JSX
По умолчанию React DOM экранирует все значения, включённые в JSX перед тем как отрендерить их. Всё преобразуется в строки, перед тем как быть отрендеренным. Это помогает предотвращать атаки межсайтовым скриптингом (XSS).
Рендеринг элементов
Обычно в приложениях, написанных полностью на React, есть только один корневой элемент. При встраивании React в существующее приложение вы можете рендерить во столько независимых корневых элементов, во сколько посчитаете нужным.
Компоненты React
Всегда называйте компоненты с заглавной буквы. Если компонент начинается с маленькой буквы, React принимает его за DOM-тег. Например, <div />
это div-тег из HTML, а <Welcome />
это уже наш компонент Welcome
, который должен быть в области видимости.
В приложениях, написанных на React с нуля, как правило, есть один компонент App
, который находится на самом верху. В случае, если вы переписываете существующее приложение на React, имеет смысл начать работу с маленького компонента типа Button
и постепенно двигаться «вверх» по иерархии.
Props следует называть так, чтобы они имели смысл в первую очередь с точки зрения самого компонента, а уже во вторую тех компонентов, которые его рендерят.
Если какая-то часть интерфейса многократно в нём повторяется или сама по себе достаточно сложная, имеет смысл её вынести в независимый компонент.
React-компоненты обязаны вести себя как чистые функции по отношению к своим props.
Хуки React для функциональных компонентов
useState()
useEffect()
useRef()
useMemo()
useCallback()
useContext()
Состояние компонентов
Существует вариант вызова setState()
, который принимает функцию, а не объект. Эта функция получит предыдущее состояние в качестве первого аргумента и значения пропсов непосредственно во время обновления в качестве второго аргумента
Если состояние представляет из себя объект с несколькими независимыми полями, их можно обновлять поверхностно. При вызове setState()
допустимо передать объект, в котором обновляется только одно из полей. В таком случае остальные поля не исчезнут, а просто останутся неизменными.
Обработка событий
При обращении к this
в JSX-колбэках необходимо учитывать, что методы класса в JavaScript по умолчанию не привязаны к контексту. Рекомендуется делать привязку в конструкторе с помощью bind
или использовать синтаксис полей классов, чтобы избежать проблем с производительностью.
Списки и ключи
«Ключ» — это специальный строковый атрибут, который нужно указывать при создании списка элементов.
Ключи помогают React определять, какие элементы были изменены, добавлены или удалены. Их необходимо указывать, чтобы React мог сопоставлять элементы массива с течением времени.
Лучший способ выбрать ключ — это использовать строку, которая будет явно отличать элемент списка от его соседей.
Не рекомендуется использовать индексы как ключи, если порядок элементов может поменяться. Это негативно скажется на производительности и может вызвать проблемы с состоянием компонента.
Ключи нужно определять с помощью атрибута key
непосредственно внутри функции высшего порядка, которая генерирует массив элементов.
Ключи внутри массива должны быть уникальными только среди своих соседних элементов. Им не нужно быть уникальными глобально. Можно использовать один и тот же ключ в двух разных массивах.
Ключи служат подсказками для React, но они никогда не передаются в ваши компоненты. Если в компоненте нужно то же самое значение, то передайте его явно через prop с другим именем.
Формы
В React HTML-элементы формы ведут себя немного иначе по сравнению с DOM-элементами, так как у элементов формы изначально есть внутреннее состояние.
По умолчанию браузер переходит на другую страницу при отправке HTML-форм, в том числе и этой. Если вас это устраивает, то не надо ничего менять, в React формы работают как обычно. Однако, чаще всего форму удобнее обрабатывать с помощью JavaScript-функции, у которой есть доступ к введённым данным. Стандартный способ реализации такого поведения называется «управляемые компоненты».
Управляемые компоненты
В HTML элементы формы, такие как <input>
, <textarea>
и <select>
, обычно сами управляют своим состоянием и обновляют его когда пользователь вводит данные. В React мутабельное состояние обычно содержится в свойстве компонентов state
и обновляется только через вызов setState()
.
Мы можем скомбинировать оба подхода и сделать состояние React-компонента «единственным источником правды». Тогда React-компонент будет рендерить форму и контролировать её поведение в ответ на пользовательский ввод. Значение элемента формы input
в этом случае будет контролировать React, а сам элемент будет называться «управляемый компонент».
В управляемом компоненте значение поля ввода всегда определяется состоянием React. Хотя это означает, что вы должны написать немного больше кода, теперь вы сможете передать значение и другим UI-элементам или сбросить его с других обработчиков событий.
Textarea
В React <textarea>
использует атрибут value
. Таким образом, форму с <textarea>
можно написать почти тем же способом, что и форму с однострочным <input>
.
Select
Для отображения выбранного элемента списка React вместо атрибута selected
в li
использует value
в корневом теге select
. В управляемом компоненте так удобнее, потому что обновлять значение нужно только в одном месте (state
).
В атрибут value
можно передать массив, что позволит выбрать несколько опций в теге select
.
Подводя итог, <input type="text">
, <textarea>
, и <select>
работают очень похоже. Все они принимают атрибут value
, который можно использовать, чтобы реализовать управляемый компонент.
Обработка нескольких элементов input
Если вам нужны несколько управляемых элементов input
, вы можете назначить каждому из них атрибут name
, что позволит функции-обработчику решать, что делать, основываясь на значении event.target.name
.
Здесь уместно использовать вычисляемые имена свойств, чтобы обновить значение в state
через ключ, который соответствует атрибуту name
элемента input
.
Подъем состояния
В React совместное использование состояния достигается перемещением его до ближайшего предка компонентов, которым оно требуется. Это называется «подъём состояния».
Для любых изменяемых данных в React-приложении должен быть один «источник истины». Обычно состояние сначала добавляется к компоненту, которому оно требуется для рендера. Затем, если другие компоненты также нуждаются в нём, вы можете поднять его до ближайшего общего предка. Вместо того, чтобы пытаться синхронизировать состояние между различными компонентами, вы должны полагаться на однонаправленный поток данных.
Если что-то может быть вычислено из props или из состояния, то скорее всего оно не должно находиться в состоянии.
Подъём состояния в родительский компонент — обычное дело при рефакторинге React-компонентов.
Философия React
Одна из особенностей React — это предлагаемый им процесс мышления при создании приложений.
Шаг 1. Разделение интерфейса на составляющие
Первое, что нужно сделать — выделить отдельные компоненты (и подкомпоненты) в макете и дать им имена.
Но как узнать, что стоит делать отдельным компонентом, а что нет? Используйте тот же подход, как при решении создать простую функцию или целый объект. Можно применить принцип единственной ответственности: каждый компонент должен заниматься какой-то одной задачей. Если функциональность компонента увеличивается с течением времени, его следует разбить на более мелкие подкомпоненты.
В результате должен появиться список, определяющий иерархию компонентов в приложении, например:
- App
- ParentComponent1
- ChildComponent1
- ChildComponent2
- ParentComponent2
- ParentComponent1
Шаг 2. Создание статического приложения на React
Теперь, когда все компоненты расположены в иерархическом порядке, пришло время воплотить в жизнь наше приложение. Самый лёгкий способ — создать версию, которая использует модель данных и рендерит интерфейс, но не предполагает никакой интерактивности. Разделять эти процессы полезно. Написание статического приложения требует много печатания и совсем немного мышления. С другой стороны, создание интерактивного приложения подразумевает более глубокий мыслительный процесс и лишь долю рутинной печати.
Чтобы построить статическое приложение, отображающее модель данных, нам нужно создать компоненты, которые используют другие компоненты и передают данные через props.
Более простые приложения удобнее начать с компонентов, находящихся выше по иерархии. В более сложных приложениях удобнее в первую очередь создавать и тестировать подкомпоненты.
В конце этого шага у вас на руках появится библиотека повторно используемых компонентов, отображающих вашу модель данных.
Шаг 3. Определение минимального, но полноценного отображения состояния интерфейса
Чтобы правильно построить приложение, сначала нужно продумать необходимый набор данных изменяемого состояния.
Определите минимально необходимое состояние, которое нужно вашему приложению, всё остальное вычисляйте при необходимости.
Далее следует рассмотреть модель данных по частям и определить, что должно храниться в состоянии. Задайте себе три вопроса:
- Передаются ли они от родителя через пропсы? Если так, тогда эти данные не должны храниться в состоянии компонента.
- Остаются ли они неизменными со временем? Если так, тогда их тоже не следует хранить в состоянии.
- Можете ли вы вычислить их на основании любых других данных в своём компоненте или пропсов? Если так, тогда это тоже не состояние.
Шаг 4. Определение места для хранения состояния
Итак, мы определили минимальный набор состояний приложения. Далее нам нужно выяснить, какой из компонентов владеет состоянием или изменяет его.
Помните: в React поток данных односторонний и сходит сверху вниз в иерархическом порядке. Сначала может быть не совсем ясно, какой из компонентов какое состояние должен хранить.
Для каждой части состояния в приложении:
- Определите компоненты, которые рендерят что-то исходя из этого состояния.
- Найдите общий главенствующий компонент (компонент, расположенный над другими компонентами, которым нужно это состояние).
- Либо общий главенствующий компонент, либо любой компонент, стоящий выше по иерархии, должен содержать состояние.
- Если вам не удаётся найти подходящий компонент, то создайте новый исключительно для хранения состояния и разместите его выше в иерархии над общим главенствующим компонентом.
Шаг 5. Добавление обратного потока данных
Пока что наше приложение рендерится в зависимости от props и состояния, передающихся вниз по иерархии. Иногда требуется обеспечить поток данных в обратную сторону. Например, если интерфейс содержит форму и каждый ее элемент представлен в виде отдельного компонента. В таком случае для обновления состояния родительского компонента необходимо передать callback-функцию как prop в дочерний компонент при рендере. Эта функция и будет реализовывать механизм обратного потока данных.