nmk

Лекція №4 (2 години). JSX: Синтаксис, обмеження та можливості

План лекції

  1. Відмова від шаблонізаторів на користь розширення мови
  2. Архітектура трансформації: механізми Babel та SWC, аналіз AST та еволюція рантаймів
  3. Граматика та правила побудови дерев: теорія вузлів, фрагменти та концепція єдиного кореня
  4. Динамічне наповнення та вирази: інтеграція бізнес-логіки у декларативний опис інтерфейсу
  5. Порівняльний аналіз JSX та HTML: семантичні відмінності, атрибути та об’єктна модель стилів
  6. Безпека та цілісність даних: автоматичне екранування, захист від XSS та внутрішні механізми Symbol
  7. Просунуті можливості та інновації React 19: метадані, автоматичне підняття тегів та оновлена робота з посиланнями
  8. JSX як інструмент проектування компонентів: патерни умовного відображення та ітерації

Перелік умовних скорочень

Вступ

Синтаксис JSX — це візитна картка екосистеми React. З першого погляду здається, що це просто “HTML всередині JavaScript”, однак технічно — це набагато більше. JSX стирає межу між логікою (JS) та представленням (Markup), перетворюючи декларативний опис інтерфейсу на набір математичних викликів функцій. Під час цієї лекції ми заглянемо “під капот” JSX, розберемо, як саме він компілюється, які має апаратні та логічні обмеження, і яких стандартів безпеки дотримується.


1. Відмова від шаблонізаторів на користь розширення мови

Історично веб-фреймворки (Angular, Vue, Ember) та бекенд-системи (PHP Blade, Python Jinja) використовували шаблонізатори. Шаблонізатори впроваджують “міні-мову” (з власними директивами типу ng-if, v-for) всередині звичайних HTML або стрінг-файлів. Головний недолік шаблонів — розробнику доводиться вивчати нову специфічну мову для кожного фреймворку, і ця мова дуже обмежена в порівнянні зі справжнім JavaScript.

React пішов іншим шляхом: він не став розширювати HTML, щоб дати йому можливість розуміти змінні. Він розширив сам стрінг JavaScript, щоб він розумів HTML-подібний синтаксис. Цей підхід дозволяє використовувати всю потужність об’єктно-орієнтованого та функціонального програмування JS (наприклад, методи масивів .map(), .filter()) прямо під час побудови UI.


2. Архітектура трансформації: механізми Babel та SWC, аналіз AST та еволюція рантаймів

Браузери НЕ РОЗУМІЮТЬ JSX. Якщо ви відправите у браузер код із <div />, консоль видасть помилку синтаксису. Тому JSX вимагає етапу компіляції (трансляції).

2.1. Етапи компіляції та роль AST

Інструменти на кшталт Babel або SWC читають ваш код і будують AST (Абстрактне синтаксичне дерево). Побачивши JSX-вузол у дереві, вони замінюють його на виклик JS-функцій.

2.2. Еволюція рантаймів: від createElement до автоматичного режиму

До React 17 (Класичний режим): Щоб компілятор міг перекласти <div /> у виклик React.createElement('div'), ми завжди мусили явно імпортувати React у кожен файл.

import React from "react"; // Обов'язково!
const el = <h1>Hello</h1>;
// Компілювалося в:
// const el = React.createElement('h1', null, 'Hello');

React 17+ (Новий JSX Трансформатор): Компілятори суттєво змінились. Тепер вони автоматично ін’єктують (вставляють) спеціальні рантайм-функції. Імпорт React більше не потрібен (якщо ви не юзаєте Хуки).

const el = <h1>Hello</h1>;
// Компілюється в спеціальний оптимізований виклик:
import { jsx as _jsx } from "react/jsx-runtime";
const el = _jsx("h1", { children: "Hello" });

3. Граматика та правила побудови дерев: теорія вузлів, фрагменти та концепція єдиного кореня

3.1. Обмеження єдиного батьківського елемента

Функція return в JavaScript може повертати лише одне значення (об’єкт, рядок тощо). Оскільки кожен тег JSX перетворюється на виклик функції, компонент не може повернути два брати-теги без “батька” одночасно.

// ПОМИЛКА: Adjacent JSX elements must be wrapped in an enclosing tag.
function Profile() {
  return (
    <h2>John Doe</h2>
    <p>Admin</p>
  );
}

3.2. Фрагменти як засіб очищення DOM

Щоб загорнути елементи без створення зайвого <div> в реальному HTML браузера, використовуються фрагменти: <React.Fragment> або їхній короткий запис <>.

function Profile() {
  return (
    <>
      <h2>John Doe</h2>
      <p>Admin</p>
    </>
  );
}

Примітка: Короткий запис <> не підтримує атрибути (наприклад key). Тому в списках (map) доводиться використовувати <React.Fragment key={id}>.

3.3. Капіталізація та ідентифікація типів

Як React відрізняє звичайний HTML-тег від вашого кастомного компонента? За великою літерою.


4. Динамічне наповнення та вирази: інтеграція бізнес-логіки у декларативний опис інтерфейсу

4.1. Вирази проти інструкцій (Expressions vs Statements)

Фігурні дужки {} в JSX дозволяють “відкрити вікно” назад у світ звичайного JavaScript. Але є жорстке правило: всередині дужок можна писати лише вирази (Expressions) (те, що повертає значення). Писати інструкції (Statements) (наприклад if, for, switch) — заборонено.

// ПРАВИЛЬНО (Вирази):
<p>2 + 2 = {2 + 2}</p>
<p>{user.firstName.toUpperCase()}</p>
<div>{isLogged ? "Yes" : "No"}</div>

// НЕПРАВИЛЬНО (Викличе синтаксичну помилку):
// <div>{ if(true) { return "Yes" } }</div>

4.2. Робота з ігнорованими типами

React ігнорує (не рендерить в екран) значення: false, null, undefined та true. Це дуже корисно для умовного рендерингу (про це в розділі 8). Однак, нуль 0 — рендериться! Будьте обережні: { array.length && <List />} коли масив порожній, на екран виведеться 0. Правильно: { array.length > 0 && <List /> }.


5. Порівняльний аналіз JSX та HTML: семантичні відмінності, атрибути та об’єктна модель стилів

JSX не є HTML, він набагато ближчий до JavaScript (оскільки він в ньому живе). Тому він дотримується JS-стандартів.

5.1. Атрибути в стилі camelCase

Оскільки class та for є зарезервованими словами в JavaScript, JSX використовує власний синтаксис для атрибутів:

5.2. Модель інлайнових стилів

В HTML атрибут style приймає звичайний текст. В JSX — це об’єкт JavaScript з ключами в стилі camelCase.

// HTML
<div style="background-color: red; font-size: 14px;"></div>

// JSX (Дві пари дужок: зовнішні для 'відкриття' вікна в JS, внутрішні - це сам Об'єкт)
<div style=></div>

(Примітка: для кращої продуктивності у великих проектах інлайн-стилів зазвичай уникають на користь CSS Modules або Tailwind).


6. Безпека та цілісність даних: автоматичне екранування, захист від XSS та внутрішні механізми Symbol

6.1. Автоматичне екранування значень

Кожного разу, коли ви рендерите дані через змінні (наприклад {userInput}), React конвертує все в рядок (String) ДО моменту вставки в об’єкт Virtual DOM. Це гарантує неможливість найбільш поширених XSS (Cross-Site Scripting) атак.

const badScript = "<script>alert('Hacked!');</script>";
// React виведе цей текст як ЗВИЧАЙНИЙ текст на екран, а скрипт НЕ виконається.
return <div>{badScript}</div>;

6.2. Механізм $$typeof та захист від підробки об’єктів

Всі React об’єкти (елементи) мають вбудовану властивість $$typeof: Symbol.for('react.element'). Символи (Symbol) неможливо передати через JSON з бекенду. Якщо хакер спробує передати готовий об’єкт (який схожий на React-вузол) з сервера в надії, що фронтенд відрендерить його і виконає шкідливий код, додаток просто крашнеться, тому що об’єкту з сервера не вистачатиме безпечного маркера $$typeof.

6.3. Використання dangerouslySetInnerHTML та санітизація

Іноді вам ПОТРІБНО відрендерити “брудний” HTML (наприклад, з редактора тексту, як-от TinyMCE). У такому разі ви маєте на свій страх і ризик відключити захист React:

const articleBody = "<h1>Real HTML from DB</h1>";
// Розробник гарантує, що HTML безпечний (наприклад, пропущений через DOMPurify)
return <div dangerouslySetInnerHTML= />;

7. Просунуті можливості та інновації React 19: метадані, автоматичне підняття тегів та оновлена робота з посиланнями

З приходом 19-ї версії React, розробка стала ще більш декларативною.

До версії 19, щоб змінити <title> сторінки із компоненту, потрібно було використовувати бібліотеку react-helmet. Тепер це працює нативно: якщо React бачить мета-теги всередині ваших компонентів, він автоматично піднімає їх у секцію <head> документа.

function BlogPost({ title }) {
  return (
    <article>
      {/* Цей тег автоматично підніметься у <head> сторінки */}
      <title>{title} - My Blog</title>
      <h1>{title}</h1>
    </article>
  );
}

7.2. Революція у роботі з ref

Раніше, щоб прокинути ref-властивість всередину кастомного компонента, доводилося використовувати спеціальну обгортку forwardRef. У React 19 компонент може приймати ref як звичайний пропс поруч з іншими, значно спрощуючи побудову дизайн-систем.

// React 19
const CustomInput = ({ ref, placeholder }) => {
  return <input ref={ref} placeholder={placeholder} />;
};

8. JSX як інструмент проектування компонентів: патерни умовного відображення та ітерації

8.1. Патерни умовного рендерингу

Оскільки ми не можемо використовувати if/else всередині JSX, розробники використовують 3 головні патерни:

Патерн Логічне І (&&) - коли потрібна умова “якщо так — малюємо, якщо ні — ігноруємо”:

{
  unreadMessages.length > 0 && (
    <span className="badge">You have new messages</span>
  );
}

Тернарний оператор (? : ) - коли є дві гілки розвитку:

{
  isLoading ? <Spinner /> : <Dashboard />;
}

Раннє повернення (Early Return) - винос імперативного if вище, за межі JSX:

function SecurePage({ user }) {
  if (!user.isAuthed) {
    return <LoginPrompt />; // JSX взагалі не дійде до коду нижче
  }
  return <SecretData />;
}

8.2. Коментарі в JSX

Оскільки JSX — це суміш, коментарі в ньому також пишуться по-особливому (як багаторядковий коментар JS всередині виразових дужок {}).

<div>
  {/* Цей коментар не буде видно в інспекторі браузера (DOM) */}
  <h1>Header</h1>
</div>

Висновки

  1. JSX є фундаментом декларативної архітектури React, який інтегрує розмітку прямо в JavaScript, відкидаючи недоліки традиційних шаблонізаторів.
  2. Процес трансляції безперервно еволюціонує: починаючи з класичного перетворення на React.createElement до швидкого jsx-runtime у сучасних збирачах.
  3. Важливо пам’ятати про обмеження єдиного кореня (Single Root Node) та використовувати Фрагменти <> для запобігання переповненню DOM “div-супом”.
  4. Синтаксис JSX має ряд відмінностей від атрибутів HTML для відповідності стандартам DOM (наприклад, className, htmlFor та camelCase-події).
  5. React має потужну вбудовану систему захисту від XSS атак на етапі рендерингу завдяки механізмам автоматичного екранування змінних та перевірці Symbol $$typeof.

Джерела

  1. Офіційна документація React. “Writing Markup with JSX”. URL: https://react.dev/learn/writing-markup-with-jsx
  2. React Docs. “JavaScript in JSX with Curly Braces”. URL: https://react.dev/learn/javascript-in-jsx-with-curly-braces
  3. “Introducing the New JSX Transform”. React Blog. URL: https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html
  4. Babel REPL — інтерактивний перегляд трансляції JSX. URL: https://babeljs.io/repl
  5. “What is XSS (Cross Site Scripting)?”. OWASP. URL: https://owasp.org/www-community/attacks/xss/
  6. Dan Abramov. “Why Do React Elements Have a $$typeof Property?”. Overreacted Blog. URL: https://overreacted.io/why-do-react-elements-have-typeof-property/
  7. React 19 Upgrade Guide та Release Notes (Офіційний блог).
  8. “Expressions vs Statements in JavaScript” (Kyle Simpson, YDKJS).
  9. “Conditionally Rendering in React”. URL: https://react.dev/learn/conditional-rendering
  10. MDN Web Docs. “HTMLElement.style”. URL: https://developer.mozilla.org/

Запитання для самоперевірки

  1. Які проблеми традиційних шаблонізаторів (як-от в Angular або Vue) вирішує підхід із написанням JSX?
  2. Поясніть причину появи помилки “Adjacent JSX elements must be wrapped in an enclosing tag” та правильні способи її вирішення.
  3. У чому полягає різниця між використанням class в HTML та className в JSX? Чому була запроваджена така зміна?
  4. Надайте пояснення тому факту, чому використання інструкцій (наприклад if-else або циклу for) безпосередньо у фігурних дужках {} всередині JSX викликає краш компілятора?
  5. Яким чином React захищає додаток від виконання користувачем шкідливого скрипту, який він ввів у поле коментаря, і яке значення має властивість $$typeof у захисті архітектури?