Рендеринг и JSX
Что это такое
Компонент React — это функция, возвращающая описание части пользовательского интерфейса. Описание записывают на JSX: синтаксис выглядит как HTML, но на деле представляет собой короткую запись вызовов функций. React берёт это описание, сравнивает его с предыдущим и приводит DOM браузера в соответствие.
Почему это важно
Прямая работа с DOM императивна: приходится помнить каждый созданный узел, каждый добавленный класс и каждый навешенный обработчик, а потом снимать всё это в правильном порядке. React переворачивает картину: вы пишете разметку, которая должна существовать при текущих данных, а согласованность DOM с ней поддерживает уже сам React. Поэтому экран становится функцией состояния, а не последовательностью правок, и представление React можно перерисовывать десять раз в секунду — код от этого не станет нечитаемым. Классическая ошибка, когда значение отображается правильно один раз и больше не обновляется, обходит стороной тех, кто освоил модель рендеринга сразу.
Ключевые идеи
Компонент — это функция, возвращающая JSX
Назовите её с заглавной буквы и верните один корневой элемент — React вызовет функцию, получит дерево элементов и отрисует его. Больше ничего не нужно: ни регистрации, ни базового класса.
function Badge({ label }) {
return <span className="badge">{label}</span>;
}
Компоненты-классы — старая запись той же идеи. В существующем коде они ещё встречаются, однако новый код пишет функции.
JSX — это JavaScript, а не HTML
Поскольку JSX компилируется в вызовы функций, внутри фигурных скобок помещается любое выражение JavaScript. Атрибуты следуют именованию JavaScript, поэтому class становится className, и каждый элемент нужно закрывать — включая <img /> и <input />. Имя компонента в угловых скобках — это ссылка на переменную, и именно поэтому заглавная буква так важна.
function Greeting({ name, unread }) {
return (
<p title={`непрочитанных: ${unread}`}>
Привет, {name}! У вас {unread} нов{unread === 1 ? "ое" : "ых"}.
</p>
);
}
Рендеринг — чистое вычисление
Рендер не должен менять ничего вне себя: ни записей в базу данных, ни мутаций свойств, ни инкрементов счётчика. При тех же свойствах и том же состоянии результат обязан совпасть. Именно эта чистота позволяет React свободно перерисовывать дерево, и на этом правиле держится каждый следующий хук. Побочным эффектам место в эффекте — уже после того, как рендер завершился.
React сравнивает дерево, поэтому важна идентичность
После рендера React сравнивает новое дерево элементов с предыдущим и применяет только различия. В списке ему нужна устойчивая идентичность, чтобы отличить перемещённый элемент от изменённого, и её даёт свойство key.
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
Индекс массива в роли ключа кажется безобидным ровно до первой вставки, переупорядочивания или удаления: тогда React переиспользует не тот узел DOM, и ошибка всплывёт в совсем другом месте.
Практика
Отрисуйте значение, которое обновляется
Соберите компонент, который принимает температуру в градусах Цельсия как свойство и показывает её и в Цельсиях, и в Фаренгейтах. Отрисуйте его три раза на одной странице с разными значениями.
Готово, когда: все три экземпляра показывают правильную пару чисел, и вы можете указать на ту единственную строку, которая делает преобразование.
Сломайте список намеренно
Отрисуйте список из пяти элементов, используя индекс массива как ключ. Добавьте кнопку, удаляющую второй элемент, при этом каждая строка показывает собственное текстовое поле. Введите что-нибудь в третью строку, затем удалите вторую.
Готово, когда: вы увидели, что текст потеряла не та строка, и можете объяснить, почему key={item.id} это исправляет.
Частые ошибки
- Имя компонента со строчной буквы — React принимает такой тег за встроенный HTML-элемент и молча не рендерит ничего полезного; исправить: начинайте имена компонентов с заглавной буквы.
- Изменение свойств или состояния во время рендера — экран сохраняет значение, которое должен был заменить, а причину при этом не видно; исправить: вычисляйте новое значение вместо записи в старое.
- Индекс массива как ключ — вставка или переупорядочивание строк переносит состояние в не тот элемент; исправить: используйте устойчивый id, переживающий изменение.
- Дословная запись HTML-атрибутов —
class,forи имена встроенных событий не переживают преобразование JSX; исправить: используйтеclassName,htmlForи функцию для каждого обработчика. - Несколько соседних элементов без обёртки — компонент падает, ведь у JSX один корень; исправить: оберните их во фрагмент.
Что читать дальше
- JavaScript (02-frontend/06-javascript) — если стрелочные функции, деструктуризация или spread-синтаксис в примерах ещё не стали второй натурой, прочитайте сначала это.
- Компоненты и свойства (02-frontend/29-react/02-components-and-props) — следующий шаг: разбить представление на компоненты, которые компонуются между собой.
- Веб-производительность (Core Web Vitals) (02-frontend/26-web-performance-core-web-vitals) — чего стоит повторный рендеринг, когда страница уже в продакшене.