React
JSX: разметка, которая на самом деле JavaScript
Первое, что отталкивает в React новичка: HTML лежит прямо внутри JavaScript. Выглядит как нарушение всех правил, которым учили десять лет. Но через час работы происходит щелчок: это не HTML внутри JS, это JS, который только притворяется HTML. Любой тег - это вызов функции, любой блок в фигурных скобках - обычное выражение. И как только это понятно, разметка перестаёт быть отдельным языком и становится кодом, с которым работают все привычные инструменты.
- Любой React-компонент в Meta, Netflix, Vercel написан на JSX - это де-факто способ описывать UI в экосистеме
- TypeScript-вариант TSX даёт автодополнение и проверку типов прямо в разметке, чего обычные шаблоны не умеют
- Vue и Svelte используют свои шаблоны, но и они поддерживают JSX как опцию - идея оказалась влиятельной
- Сборщики (Vite, SWC, Babel) превращают JSX в обычные вызовы функций ещё до того, как код попадёт в браузер
Предварительные знания
- Урок про модель React (UI = f(state))
- JavaScript-выражения: тернарный оператор, логическое И (&&), метод map у массивов
- Понимание разницы между выражением (возвращает значение) и инструкцией
Откуда взялся синтаксис JSX
JSX вырос из XHP - расширения PHP в Facebook, которое позволяло писать HTML-теги прямо в PHP-коде с защитой от XSS. Когда Джордан Уолк переносил идею в браузер, он сохранил тот же приём: разметка как часть языка, а не как строки. Намеренно было решено не делать JSX обязательным - технически можно писать React и без него, вызывая createElement руками. Но почти никто так не делает: JSX оказался настолько удобнее, что стал лицом всей библиотеки.
JSX - это вызовы функций под капотом
JSX не понимается браузером напрямую. Перед запуском сборщик (Vite, Babel или SWC) переписывает каждый тег в вызов функции. Тег превращается в элемент - лёгкий объект с типом, props и детьми. Именно эти объекты React потом сравнивает при reconciliation. Поэтому JSX это не шаблонизатор, а удобная запись для создания объектов.
Раз тег это объект, его можно положить в переменную, вернуть из функции, передать в массив. JSX-элемент - полноправное значение языка. Это отличает React от шаблонов вроде Handlebars, где разметка живёт в строках и недоступна инструментам кода.
Современный JSX-transform (с React 17) не требует импортировать React в каждый файл: сборщик сам подставляет нужную функцию из react/jsx-runtime. Поэтому в новом коде строки import React встречаются всё реже.
Во что превращается JSX-тег к моменту запуска в браузере?
Чем JSX отличается от HTML
Так как JSX это JavaScript, у него свои правила имён. Слово class зарезервировано в JS, поэтому CSS-класс задаётся как className. Атрибуты пишутся в camelCase: onClick, onChange, htmlFor, tabIndex. Все теги обязаны закрываться, включая одиночные: img и br пишутся как самозакрывающиеся.
| HTML | JSX | Почему |
|---|---|---|
| class="box" | className="box" | class - зарезервированное слово в JavaScript |
| onclick="..." | onClick={...} | Обработчики в camelCase, значение - функция, не строка |
| <br> | <br /> | Любой тег обязан закрываться |
| for="id" | htmlFor="id" | for - тоже ключевое слово в JS |
Ещё одно правило: компонент возвращает один корневой элемент. Если нужно вернуть несколько соседних узлов без лишнего div в DOM, их оборачивают во Fragment - пустой тег <>...</>. Он группирует элементы, но сам в разметку не попадает.
Почему в JSX CSS-класс задаётся через className, а не class?
Выражения, условия и списки внутри JSX
В фигурных скобках можно вычислить любое JS-выражение и вставить его результат в разметку: переменную, вызов функции, обращение к свойству. Важное ограничение: внутри скобок живут именно выражения, а не инструкции. if и for туда не поставить - вместо них работают тернарный оператор и логическое И.
Список рендерится методом map: массив данных превращается в массив JSX-элементов. Каждому элементу списка нужен атрибут key - стабильный идентификатор, по которому React понимает, какой элемент к какому относится между перерисовками. Эта тема разобрана подробно в следующем уроке, но привычку ставить key стоит выработать сразу.
Распространённая ловушка с &&: если слева число 0, React отрендерит сам 0 на экране. Поэтому условие лучше делать булевым: items.length > 0 && ..., а не items.length && ...
Почему внутри фигурных скобок JSX нельзя написать обычный if?
Связь с другими темами
JSX - это кирпич. Дальше из него строят компоненты и динамику:
- Компоненты и props — JSX-элементы группируются в компоненты, которые принимают данные через props
- Списки и keys — Рендер массива данных через map - типичное использование выражений в JSX
Итог
- JSX - это синтаксический сахар: каждый тег компилируется в вызов вроде createElement и в итоге становится обычным JavaScript-объектом
- В фигурных скобках можно писать любое JS-выражение, но не инструкции (if, for) - для условий используют тернарник и &&
- Отличия от HTML: атрибут class пишется как className, атрибуты в camelCase (onClick, htmlFor), теги обязаны закрываться
- Компонент возвращает один корневой узел - несколько элементов оборачивают во Fragment (<>...</>)
- Так как JSX это код, в нём работают типы (TSX), автодополнение и обычная отладка
Связанные уроки
- rc-01-why-react — JSX - это конкретное воплощение идеи UI = f(state) из вводного урока
- rc-03-components-props — JSX описывает один элемент, а компоненты собирают из этих элементов переиспользуемые блоки
- rc-04-rendering-lists-keys — Списки через map и атрибут key - прямое продолжение выражений в JSX