Що таке fragment у React
React Fragment - це компонент, який групує кілька JSX-елементів в один return без додавання жодного вузла до DOM.
Теорія
TL;DR
- Аналогія: харчова плівка навколо продуктів - тримає їх разом при переносі, але зникає при розпакуванні, у фінальному HTML її немає
<div>додає реальний елемент; Fragment не додає нічого- Короткий синтаксис
<>і повний<Fragment>дають однаковий результат; тільки<Fragment>приймаєkey - Потрібен один корінь і чистий DOM: Fragment. Потрібен wrapper зі стилями або обробником:
<div>
Швидкий приклад
// div ламає розмітку таблиці
function BadRow() {
return (
<div>
<td>Ім'я</td>
<td>Email</td>
</div>
);
}
// DOM: <div><td>Ім'я</td><td>Email</td></div> (невалідний HTML)
// Fragment тримає DOM чистим
function GoodRow() {
return (
<>
<td>Ім'я</td>
<td>Email</td>
</>
);
}
// DOM: <td>Ім'я</td><td>Email</td>React вимагає, щоб кожен компонент повертав один кореневий елемент. Fragment виконує цю умову, але в HTML браузера не з'являється.
Головна різниця
<div> вирішує проблему «одного кореня», але додає реальний вузол до DOM. Цей вузол має наслідки: ламає дочірні <td> всередині <tr>, порушує flex і grid, додає зайву вкладеність у семантичний HTML. Fragment вирішує ту саму задачу без жодних витрат на DOM.
Коли що використовувати
- Кілька сусідніх елементів з одного компонента: Fragment
- Рядки таблиці або елементи списку, де зайні wrapper-и ламають layout: Fragment
- Ітерація по масиву з групуванням двох сусідів на елемент:
<Fragment key={item.id}> - Wrapper потребує
className,styleабо обробник кліку:<div>або семантичний елемент
Порівняльна таблиця
| Характеристика | <div> | <Fragment> / <> |
|---|---|---|
| Додає вузли до DOM | 1 | 0 |
Підтримує key | Так | Так (тільки повний синтаксис) |
Приймає className, style | Так | Ні |
| Впливає на layout | Так | Ні |
| Коли використовувати | Потрібні стилі або події | Тільки групування |
Як це працює
Babel перетворює і <>, і <Fragment> на одне й те саме: React.createElement(React.Fragment, null, ...children). Під час reconciliation React пропускає створення DOM-вузла і монтує дочірні елементи безпосередньо в батьківський вузол. Саме тому обидва синтаксиси дають ідентичний HTML.
Типові помилки
Відсутній key на Fragment у списках:
// Неправильно: немає key, React попереджає про нестабільні ключі
items.map(item => (
<>
<li>{item.name}</li>
<li>{item.price}</li>
</>
));
// Правильно: key ставиться на Fragment, а не на дочірні елементи
items.map(item => (
<Fragment key={item.id}>
<li>{item.name}</li>
<li>{item.price}</li>
</Fragment>
));<> не приймає пропси, тому коли потрібен key, переходь на повний <Fragment>.
Очікування, що пропси спрацюють на Fragment:
// Ігнорується: Fragment не рендериться, style не має ефекту
<Fragment style={{ color: 'red' }}>
<p>Не застайловано</p>
</Fragment>Кожен проп, крім key, просто ігнорується. Якщо wrapper потребує стилів, використовуй <div>.
Зайва вкладеність:
// Надлишково: зовнішній Fragment обгортає один дочірній елемент
return (
<>
<Fragment>
<h1>Заголовок</h1>
</Fragment>
</>
);
// Поверни <h1>Заголовок</h1> напрямуДе зустрічається
- React Table v8: групування
<td>всередині<tr>без зайвих вузлів - Сторінкові компоненти Next.js: чистий JSX, щоб уникнути hydration mismatch від несподіваних wrapper-div
- Material-UI: композитні компоненти з сусідніми елементами всередині контейнера
- Умовний рендеринг сусідніх елементів без накопичення вкладених
<div>
Питання на співбесіді
Q: Яка різниця між <Fragment> і <>?
A: Результат однаковий. <> - це скорочення. Повний <Fragment> потрібен тільки для key.
Q: Чи може Fragment мати key?
A: Так, але тільки в повному синтаксисі: <Fragment key="...">. Спроба додати атрибут до <> - синтаксична помилка.
Q: Чому не замінити всі <div> на Fragment?
A: Fragment не підтримує className, style і обробники подій. Коли wrapper сам щось робить, він має бути реальним елементом.
Q: Чи видно Fragment у React DevTools?
A: Так, він відображається як вузол «Fragment» у дереві компонентів. Дочірні елементи залишаються доступними для інспекції.
Q: Як React обробляє Fragment у fiber-дереві під час reconciliation?
A: Fragment - це композитний fiber без host-компонента. Рендерер пропускає createInstance і монтує дочірні елементи безпосередньо в батьківський DOM-вузол. Проп key бере участь у diff-алгоритмі так само, як і на будь-якому іншому елементі.
Приклади
Групування комірок таблиці
Саме тут зайвий <div> найчастіше ламає верстку: layout рипається і не одразу зрозуміло чому, поки не помічаєш невалідний HTML.
function UserRow({ user }) {
return (
<tr>
<Fragment key={user.id}>
<td>{user.name}</td>
<td>{user.email}</td>
<td>
<button>Edit</button>
<button>Delete</button>
</td>
</Fragment>
</tr>
);
}
// Результат: <tr><td>ім'я</td><td>email</td><td>...</td></tr>
// Жодних зайвих вузлів, валідний HTMLkey на Fragment забезпечує стабільну реконсиляцію (reconciliation), коли UserRow використовується всередині .map().
Умовний рендеринг сусідніх елементів
function UserProfile({ user, isAdmin }) {
return (
<>
<h2>{user.name}</h2>
<p>{user.email}</p>
{isAdmin && (
<>
<p>Роль: Admin</p>
<button>Керувати користувачами</button>
</>
)}
</>
);
}Обидва рівні використовують <>. В DOM потрапляють тільки ті елементи, що реально рендеряться, жодного накопичення wrapper-ів.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.