Що таке JSX у React?
JSX - це розширення синтаксису JavaScript, яке дозволяє писати HTML-подібну розмітку прямо у JS-файлах. Babel перетворює її на виклики React.createElement під час збірки.
Теорія
TL;DR
- JSX - це HTML-синтаксис всередині JavaScript: ті самі знайомі теги, але вирази пишуть у
{} - Головна перевага:
<div>Hello</div>замістьReact.createElement('div', null, 'Hello') - Babel конвертує JSX під час збірки, без витрат у рантаймі
- Використовуй JSX у кожному React-компоненті; пропускай лише за відсутності бандлера
- JSX - це не HTML: атрибути пишуться camelCase (
className,onClick,htmlFor)
Швидкий приклад
// Без JSX (багатослівно)
const elem = React.createElement('h1', null, 'Hello, ', name, '!');
// З JSX (читабельно)
const elem = <h1>Hello, {name}!</h1>;
// Babel перетворює рядок з JSX у виклик createElement вище.
// Результат ідентичний, коду менше.Фігурні дужки {} - це перемикач між розміткою та JavaScript. Всередині них - JS-вираз: змінна, тернарний оператор, виклик функції. Звичайний текст за межами дужок залишається буквальним текстом.
Головна відмінність від HTML
JSX схожий на HTML, але відрізняється у трьох речах. Атрибути пишуться camelCase (className замість class, htmlFor замість for). Кожен тег потрібно закривати, включно з одиночними (<img />). І можна повертати лише один кореневий елемент. Саме через третє правило з'явився <>...</> (React.Fragment) - він обгортає кілька елементів без додаткового вузла у реальному DOM.
Коли використовувати
- Будь-який React-компонент → завжди JSX, без винятків
- Динамічні списки →
{todos.map(todo => <li key={todo.id}>{todo.text}</li>)} - Умовний рендеринг →
{isLoading ? <Spinner /> : <Content />} - Без бандлера (React через CDN script tag) →
React.createElementнапряму - Server-side rendering → JSX без змін у Node через
ReactDOMServer.renderToString
Що робить Babel
Плагін @babel/preset-react читає JSX під час збірки. Теги з великої літери (<TodoList />) стають посиланнями на змінну - твою функцію-компонент. Теги з малої (<ul>) стають рядковими літералами. Babel також автоматично екранує вміст, тому JSX-вирази ніколи не вставляються як сирий HTML. Це захищає від XSS за замовчуванням. Обійти можна лише через dangerouslySetInnerHTML - і назва навмисно незручна.
Типові помилки
Оператор if всередині JSX
// Неправильно - if є оператором, а не виразом
return <div>{if (isLoggedIn) <p>Hi</p>}</div>; // помилка парсера
// Правильно - тернарний оператор є виразом
return <div>{isLoggedIn ? <p>Hi</p> : null}</div>;Рендеринг масиву об'єктів без перетворення
// Неправильно - виводить "[object Object],[object Object]"
<ul>{[{name: 'Alice'}, {name: 'Bob'}]}</ul>
// Правильно
<ul>{[{name: 'Alice'}, {name: 'Bob'}].map(item => <li>{item.name}</li>)}</ul>Кілька кореневих елементів без Fragment
// Неправильно - два елементи на верхньому рівні
return (
<h1>Title</h1>
<p>Text</p>
); // синтаксична помилка
// Правильно
return (
<>
<h1>Title</h1>
<p>Text</p>
</>
);class замість className
// Неправильно - class є зарезервованим словом у JS
<div class="container">Content</div>
// Правильно
<div className="container">Content</div>Де зустрічається у продакшені
- Next.js: кожен файл у
pages/абоapp/повертає JSX-компонент - React Native: той самий синтаксис JSX, але інші теги (
<View>,<Text>замість<div>,<p>) - Remix, Gatsby: серверний рендеринг JSX з тією самою моделлю мислення
- Preact: сумісний з JSX з розміром рантайму 3 КБ
Питання на співбесіді
Q: Що робить Babel з JSX?
A: Перетворює кожен JSX-тег на виклик React.createElement. <h1>Hi</h1> стає React.createElement("h1", null, "Hi"). Результат - звичайний JS-об'єкт, що описує тип елемента та його пропси.
Q: Навіщо фігурні дужки для виразів?
A: Вони сигналізують "тут JS-режим". {2 + 2} рендерить 4. Без дужок 2 + 2 залишається рядком "2 + 2".
Q: Чи може JSX повертати кілька кореневих елементів?
A: Ні. Обгортай їх у <>...</> (React.Fragment). Fragment не з'являється у реальному DOM.
Q: Яка різниця між атрибутами JSX і HTML?
A: JSX використовує className замість class, htmlFor замість for, camelCase обробники подій (onClick замість onclick). Атрибути JSX чутливі до регістру, атрибути HTML - ні.
Q: (Senior) Як JSX обробляє spread-пропси і що відбувається при конфлікті ключів?
A: <MyComp {...props} color="blue" /> компілюється у React.createElement(MyComp, Object.assign({}, props, { color: "blue" })). Пізніші пропси перекривають ранніші. Якщо props.color дорівнює "red", фінальне значення буде "blue". Злиття поверхневе.
Приклади
Базовий: від JSX до DOM
function Greeting({ name }) {
return <h1 className="title">Hello, {name}!</h1>;
}
// Babel компілює return у:
// React.createElement('h1', { className: 'title' }, 'Hello, ', name, '!')
// Використання: <Greeting name="Alice" />
// Результат: <h1 class="title">Hello, Alice!</h1>Ти пишеш JSX, Babel виконує конвертацію, React будує DOM-вузол. Більше нічого не відбувається.
Середній: динамічний список з умовним стилем
function TodoList({ todos }) {
return (
<ul>
{todos.map(todo => (
<li key={todo.id} className={todo.completed ? 'done' : ''}>
{todo.text}
</li>
))}
</ul>
);
}
// Використання:
// <TodoList todos={[{ id: 1, text: 'Купити молоко', completed: true }]} />
// Результат: <ul><li class="done">Купити молоко</li></ul>Проп key обов'язковий при рендерингу списків. React використовує його для відстеження змін між рендерами. Без нього отримаєш попередження у консолі на кожне оновлення.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.