Skip to main content

Що таке 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
// Без 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

jsx
// Неправильно - if є оператором, а не виразом return <div>{if (isLoggedIn) <p>Hi</p>}</div>; // помилка парсера // Правильно - тернарний оператор є виразом return <div>{isLoggedIn ? <p>Hi</p> : null}</div>;

Рендеринг масиву об'єктів без перетворення

jsx
// Неправильно - виводить "[object Object],[object Object]" <ul>{[{name: 'Alice'}, {name: 'Bob'}]}</ul> // Правильно <ul>{[{name: 'Alice'}, {name: 'Bob'}].map(item => <li>{item.name}</li>)}</ul>

Кілька кореневих елементів без Fragment

jsx
// Неправильно - два елементи на верхньому рівні return ( <h1>Title</h1> <p>Text</p> ); // синтаксична помилка // Правильно return ( <> <h1>Title</h1> <p>Text</p> </> );

class замість className

jsx
// Неправильно - 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

jsx
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-вузол. Більше нічого не відбувається.

Середній: динамічний список з умовним стилем

jsx
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 використовує його для відстеження змін між рендерами. Без нього отримаєш попередження у консолі на кожне оновлення.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?