Интервью-вопрос
Что происходит с JSX при рендере
JSX сначала становится обычным JavaScript. При рендере React получает дерево React-элементов, сравнивает его с прошлым деревом и применяет к DOM только нужные изменения.
- Добавлен
- Редакция
Подготовьте короткий ответ и пару деталей на случай уточняющих вопросов.
Мини-квиз
Проверка перед разбором
Несколько быстрых вопросов перед разбором. Так проще поймать места, которые только кажутся понятными.
Вопрос 1 из 60 правильно
Разбор
Разобраться, а не зазубрить
Дальше разбираем суть, типичные уточнения и места, где легко сказать лишнее или перепутать термины.
Базовая идея
JSX это удобный синтаксис для описания UI в React. Он похож на HTML, но не является HTML и не выполняется браузером напрямую. До выполнения код проходит через компилятор, например Babel, SWC или другой инструмент сборки.
На собеседовании удобно разделить ответ на два шага. Сначала JSX превращается в обычный JavaScript. Потом React во время рендера вызывает компоненты, получает React-элементы и решает, какие изменения нужны в интерфейсе.
Короткая формулировка для ответа:
JSX компилируется в JavaScript, который создает React-элементы. React-элементы это не DOM, а описание интерфейса. При обновлении React строит новое описание, сравнивает его с прошлым и применяет нужные изменения к DOM.
Что получается после компиляции
Исторически JSX часто показывают как синтаксический сахар над React.createElement.
const title = <h1 className="title">Hello</h1>;Примерно так это можно объяснить через старую модель:
const title = React.createElement(
"h1",
{ className: "title" },
"Hello"
);В современных проектах результат может выглядеть иначе, например через функции из react/jsx-runtime. Поэтому лучше не обещать один конкретный вызов для любого проекта. Главное сказать суть: JSX исчезает как синтаксис, а вместо него остается JavaScript, который создает React-элементы.
Если вы отвечаете коротко, можно сказать так: часто JSX объясняют через React.createElement, но в новом JSX transform могут быть jsx или jsxs из react/jsx-runtime. Суть не меняется. Создаются React-элементы.
Что такое React-элемент
React-элемент это объект с описанием UI. Он говорит React, какой тип нужно отобразить, какие props передать и какие children есть внутри. Для DOM-тега типом будет строка, например "div". Для компонента типом будет функция или объект компонента.
function UserCard({ name }) {
return <section className="card">{name}</section>;
}
const element = <UserCard name="Ada" />;В этом примере JSX не создает section сразу. Сначала создается элемент с типом UserCard и props { name: "Ada" }. Потом React вызовет компонент, получит JSX внутри него и продолжит строить дерево описаний.
Практический вывод простой: render должен быть чистым. Если вы делаете побочные эффекты прямо во время рендера, React может выполнить этот код повторно или не довести работу до commit phase. Для запросов, подписок и ручной работы с DOM нужны эффекты и cleanup, а не вычисление JSX.
Как React использует результат JSX
- 1JSX компилируется в JavaScript-вызовы
- 2Компонент при рендере возвращает React-элементы
- 3React строит новое дерево и сопоставляет его с прошлым
- 4На commit phase React меняет только нужные DOM-узлы
- 1Сказать, что браузер понимает JSX
- 2Назвать React-элемент реальным DOM-узлом
- 3Смешать render phase и изменение DOM
- 4Забыть про key, type и сохранение состояния
После изменения state или props React снова вызывает нужные компоненты и получает новое дерево React-элементов. Затем он сравнивает новое дерево с прошлым. Этот процесс обычно называют reconciliation.
React смотрит на типы элементов, порядок детей, ключи и props. Если тип элемента изменился, React может размонтировать старую часть дерева и создать новую. Если тип тот же, React может переиспользовать существующий DOM-узел или компонент и обновить только изменившиеся props.
DOM меняется не в момент, когда вы написали JSX, и не обязательно при каждом вызове компонента. Реальные изменения происходят на commit phase, когда React уже решил, что именно нужно применить.
Почему key связан с рендером JSX
key не попадает в props как обычное поле. Это специальная подсказка React для сопоставления элементов в списке. Она особенно важна, когда элементы можно добавлять, удалять, сортировать или фильтровать.
Плохой пример для динамического списка:
{users.map((user, index) => (
<UserRow key={index} user={user} />
))}Если список изменит порядок, React может связать старое состояние строки с другим пользователем. Так появляются странные баги: фокус остается не там, инпут показывает чужое значение, а анимация применяется к неправильной строке.
Безопаснее использовать стабильный идентификатор данных:
{users.map((user) => (
<UserRow key={user.id} user={user} />
))}На собеседовании достаточно сказать: key помогает React понять, какой ребенок в списке остался тем же самым между рендерами.
JSX, выражения и props
Внутри JSX фигурные скобки означают JavaScript-выражение. Это выражение вычисляется во время рендера компонента, а его результат становится частью props или children.
function Greeting({ user, isAdmin }) {
return (
<h1 className={isAdmin ? "admin" : "user"}>
Hello, {user.name}
</h1>
);
}Важно: внутри {} нужно выражение, а не любой оператор. Поэтому if напрямую как значение не подойдет. Обычно используют тернарный оператор, логическое &&, заранее подготовленную переменную или выносят ветвление выше.
Практический риск здесь не только синтаксический. Если вы вызываете тяжелую функцию прямо внутри JSX, она будет выполняться при каждом рендере компонента. Иногда это нормально. Но для дорогих вычислений лучше подумать о мемоизации, кэше или переносе вычисления ближе к данным.
Практические риски во время render
Выражения в JSX выполняются при рендере. Поэтому в render нельзя незаметно прятать сетевой запрос, подписку или запись во внешнее состояние. Такой код может выполниться много раз. В итоге появляются лишние запросы, гонки данных и обновление состояния после размонтирования.
Плохой пример:
function UserCard({ id }) {
const [user, setUser] = React.useState(null);
fetch(`/api/users/${id}`)
.then((response) => response.json())
.then(setUser);
return user ? <Profile user={user} /> : <p>Загрузка...</p>;
}Что сломается: каждый рендер запускает новый запрос, ответ старого запроса может перезаписать свежие данные, а после размонтирования компонент все еще может попытаться обновить state. Безопаснее вынести запрос в эффект, обработать loading и error, а старый запрос отменять.
function UserCard({ id }) {
const [user, setUser] = React.useState(null);
const [error, setError] = React.useState(null);
React.useEffect(() => {
const controller = new AbortController();
setUser(null);
setError(null);
fetch(`/api/users/${id}`, { signal: controller.signal })
.then((response) => {
if (!response.ok) throw new Error("Request failed");
return response.json();
})
.then(setUser)
.catch((error) => {
if (error.name !== "AbortError") setError(error);
});
return () => controller.abort();
}, [id]);
if (error) return <p role="alert">Не удалось загрузить профиль</p>;
if (!user) return <p>Загрузка...</p>;
return <Profile user={user} />;
}JSX и внешние данные
Обычный вывод строки в JSX React экранирует. Если user.name содержит HTML, запись {user.name} покажет текст, а не выполнит тег или скрипт. Это помогает против XSS при обычном выводе данных.
Риск появляется, когда вы обходите эту защиту через dangerouslySetInnerHTML или ручной innerHTML. Тогда HTML из CMS, комментария или API нужно санитизировать до рендера и использовать только если без этого нельзя. На собеседовании это хороший практический вывод: JSX не равен HTML-строке, а безопасность зависит от способа вставки данных.
Что важно для Next.js
В Next.js JSX тоже компилируется. Разница в том, где выполняется часть рендера. Серверные компоненты могут быть отрендерены на сервере, а клиентские компоненты затем гидрируются в браузере. При SSR сервер может отдать HTML, но это не значит, что JSX сам стал HTML в браузере.
Если вас спрашивают про Next.js, ответ можно собрать так: JSX проходит компиляцию, затем React может использовать его результат на сервере для генерации HTML или на клиенте для интерактивного UI. На клиенте React сопоставляет разметку с деревом компонентов во время hydration. Если результат рендера на сервере и клиенте отличается, можно получить hydration mismatch.
Практический вывод
Хороший ответ на этот вопрос не должен застревать на фразе "JSX это синтаксический сахар". Добавьте, что именно создается, когда это происходит и как React потом применяет изменения.
Рабочая структура ответа:
- JSX компилируется в JavaScript до выполнения.
- JavaScript-вызовы создают React-элементы, а не DOM.
- Компоненты при рендере возвращают дерево React-элементов.
- React сравнивает новое дерево с прошлым и применяет изменения к DOM на commit phase.
key,typeиpropsвлияют на то, что будет переиспользовано, а что будет пересоздано.
Такой ответ показывает, что вы понимаете и сборку, и runtime-поведение React. Это звучит сильнее, чем просто пересказать пример с React.createElement.
Частые ошибки
Где обычно ошибаются
Проверьте формулировки, которые звучат уверенно, но на интервью быстро выдают пробелы.
- 1
Называть JSX HTML
JSX похож на HTML, но это синтаксис из JavaScript-экосистемы. У него есть отличия вродеclassName,htmlFor, JavaScript-выражений в фигурных скобках и объектов дляstyle. Если назвать JSX обычным HTML, следующим вопросом у вас могут уточнить props или выражения. - 2
Думать, что createElement создает DOM
React.createElementили функции JSX runtime создают React-элемент. Это описание будущего UI. DOM создается или обновляется позже, во время commit phase. Так вы не смешаете вычисление интерфейса с побочными эффектами в браузере. - 3
Говорить только про React.createElement
В старых материалах это частый ответ, но в современных проектах компилятор может использоватьreact/jsx-runtime. Лучше сказать шире: JSX компилируется в JavaScript-вызовы, которые создают React-элементы. После этого можно добавить, что исторически это часто объясняют черезReact.createElement. - 4
Смешивать render и commit
Во время render phase React вычисляет новое дерево. Эту работу можно прервать или повторить. Реальные изменения DOM происходят на commit phase. Если забыть это различие, легко ошибиться в вопросах про эффекты, Strict Mode и производительность. - 5
Игнорировать key в списках
keyнужен не для красоты и не только для warning. Он помогает React понять, какой элемент остался тем же самым после изменения массива. Нестабильный key может перенести состояние инпута или компонента на другой элемент списка. - 6
Делать побочные эффекты во время render
Если вызватьfetch, подписку или запись вlocalStorageпрямо при вычислении JSX, код может выполниться при каждом рендере. Это приводит к лишним запросам, гонкам данных и обновлению состояния после размонтирования. Запросы и подписки выносите вuseEffectили обработчики. Для эффектов добавляйте cleanup и отмену запроса. - 7
Думать, что JSX безопасно вставляет любой HTML
Обычный вывод строк в JSX экранируется, поэтому{user.name}не станет HTML. НоdangerouslySetInnerHTMLобходит эту защиту. Если вы рендерите внешний HTML, нужна санитизация и понятная причина. Иначе легко получить XSS.
Follow-up
Что могут спросить дальше
Короткие ответы на вопросы, которыми проверяют понимание JSX, React-элементов и рендера.
Живые ответы
Видео с похожим вопросом
Если найдем публичные интервью с таким вопросом, добавим их сюда. Их удобно смотреть после теории, чтобы свериться с живыми ответами.
Пока видео нет. Когда появятся подходящие публичные интервью, добавим их в этот блок, чтобы можно было сравнить разбор с тем, как отвечают реальные кандидаты.
Что нужно добавить в useEffect в React, чтобы снять обработчик после закрытия модального окна 😎
В useEffect нужно вернуть функцию очистки, которая удалит обработчик события. Разбираем, когда cleanup вызывается, как связать его с открытием модального окна и какие ошибки дают утечки и лишние срабатывания.
Что сможешь написать на React 😎
На этот вопрос лучше отвечать через реальные типы задач: компоненты, состояние, API, формы, маршруты, оптимизацию и тесты. Важно показать границы своего уровня и не обещать опыт, которого нет.