Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке fragment у React». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**React Fragment** групує кілька JSX-елементів без додавання жодного вузла до DOM. ```jsx // Короткий синтаксис <> <td>Ім'я</td> <td>Email</td> </> // Повний синтаксис (потрібен для key) <Fragment key={item.id}> <td>{item.name}</td> <td>{item.email}</td> </Fragment> ``` **Ключове:** `<>` для чистого групування; `<Fragment>` тільки коли потрібен `key`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**React Fragment** - це компонент, який групує кілька JSX-елементів в один `return` без додавання жодного вузла до DOM. ## Теорія ### TL;DR - Аналогія: харчова плівка навколо продуктів - тримає їх разом при переносі, але зникає при розпакуванні, у фінальному HTML її немає - `<div>` додає реальний елемент; Fragment не додає нічого - Короткий синтаксис `<>` і повний `<Fragment>` дають однаковий результат; тільки `<Fragment>` приймає `key` - Потрібен один корінь і чистий DOM: Fragment. Потрібен wrapper зі стилями або обробником: `<div>` ### Швидкий приклад ```tsx // 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 у списках:** ```tsx // Неправильно: немає 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:** ```tsx // Ігнорується: Fragment не рендериться, style не має ефекту <Fragment style={{ color: 'red' }}> <p>Не застайловано</p> </Fragment> ``` Кожен проп, крім `key`, просто ігнорується. Якщо wrapper потребує стилів, використовуй `<div>`. **Зайва вкладеність:** ```tsx // Надлишково: зовнішній 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. ```tsx 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> // Жодних зайвих вузлів, валідний HTML ``` `key` на Fragment забезпечує стабільну реконсиляцію (reconciliation), коли `UserRow` використовується всередині `.map()`. ### Умовний рендеринг сусідніх елементів ```tsx function UserProfile({ user, isAdmin }) { return ( <> <h2>{user.name}</h2> <p>{user.email}</p> {isAdmin && ( <> <p>Роль: Admin</p> <button>Керувати користувачами</button> </> )} </> ); } ``` Обидва рівні використовують `<>`. В DOM потрапляють тільки ті елементи, що реально рендеряться, жодного накопичення wrapper-ів.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.