При работе с React лучше использовать многоразовые компоненты с помощью таких инструментов, как Bit, чтобы избежать необходимости переписывать их каждый раз. К тому же, приятно использовать компоненты, организованные в визуальной галерее, не так ли?
Постановка задачи
Попробуйте визуализировать несколько компонентов React подобным образом:
class ParentC extends React.Component {
render() {
return (
<ChildC />
<ChildC />
)
}
}
class ChildC extends React.Component {
render() {
return (
<h1>
Child Component
</h1>
)
}
}
или создать подобный список узлов:
class ChildComponent extends React.Component {
render() {
return (
<h1>Hello Child Component</h1>
<h1>Hello Child Component</h1>
)
}
}
При использовании VS Code с расширениями поддержки JSX, появится предупреждение: “Родительские выражения JSX должны иметь один родительский элемент”.
Чтобы предупреждение исчезло, нужно добавить дополнительный тег div в качестве родительского элемента в разметку JSX.
class ParentC extends React.Component {
render() {
return (
<div>
<ChildComponent />
<ChildComponent />
</div>
)
}
}
class ChildComponent extends React.Component {
render() {
return (
<div>
<h1>Hello Child Component</h1>
<h1>Hello Child Component</h1>
</div>
)
}
}
Проблема заключается в том, что использовать теги div немного неудобно. В HTML встречаются случаи, когда дополнительный div может деструктурировать DOM. Например, при использовании таблицы в компонентах.
Нам нужно визуализировать данные пользователей в табличной форме с помощью HTML-элемента table. Нужно визуализировать следующий код в React:
<table>
<tr>
<th>Name</th>
<th>Occupation</th>
<th>Age</th>
</tr>
<tr>
<td>Chidume Nnamdi</td>
<td>Software Dev</td>
<td>27</td>
</tr>
<tr>
<td>Row Ekemezie</td>
<td>Software Dev</td>
<td>? :)</td>
</tr>
</table>
Создадим компоненты для визуализации каждого аспекта элемента table. HeaderComponent отобразит заголовок таблицы, BodyComponent — тело таблицы. TableComponent визуализирует каркас таблицы с HeaderComponent и BodyComponent таким образом:
class TableComponent extends React.Component {
render() {
return (
<table>
<tr>
<HeaderComponent />
</tr>
<tr>
<BodyComponent />
</tr>
</table>
)
}
}
HeaderComponent должен выглядеть так:
class HeaderComponent extends React.Component {
render() {
return (
<th>Name</th>
<th>Occupation</th>
<th>Age</th>
)
}
}
А BodyComponent выглядит вот так:
class BodyComponent extends React.Component {
render() {
return (
<td>Chidume Nnamdi</td>
<td>Software Dev</td>
<td>27</td>
)
}
}
Проблема с HeaderComponent и BodyComponent заключается в том, что они выполняют возврат нескольких узлов. React предупредит о необходимости вложить разметку во вложенный тег.
Затем выполняем подобные действия, чтобы устранить предупреждение:
class HeaderComponent extends React.Component {
render() {
return (
<div>
<th>Name</th>
<th>Occupation</th>
<th>Age</th>
</div>
)
}
}
class BodyComponent extends React.Component {
render() {
return (
<div>
<td>Chidume Nnamdi</td>
<td>Software Dev</td>
<td>27</td>
</div>
)
}
}
Разметка вложена в тег div. Теперь на выходе компонент Table будет выглядеть так:
<table>
<tr>
<div>
<th>Name</th>
<th>Occupation</th>
<th>Age</th>
</div>
</tr>
<tr>
<div>
<td>Chidume Nnamdi</td>
<td>Software Dev</td>
<td>27</td>
</div>
</tr>
</table>
Выше приведен неправильный вывод элемента table. Элемент div не должен быть отображен. Компоненты React предназначены для возврата элементов, но они должны быть заключены в родительский тег, несколько элементов не могут быть возвращены. Однако добавление дополнительного узла иногда приводит к неправильному форматированию html-элемента output, как показано выше.
Как решить эту проблему? Каким образом можно возвратить заключенные элементы JSX, не влияя на визуализированный вывод в DOM?
Фрагменты React способны решить эту проблему!
Решение — Фрагменты
С помощью фрагментов можно сгруппировать список дочерних элементов без добавления дополнительных узлов в DOM.
Используя <React.Fragment>...</React.Fragment>
, можно добавить родительский тег к элементам JSX без добавления дополнительного узла к DOM. React.Fragment не выводит HTMLElement.
Приступим к решению первой проблемы:
class ParentC extends React.Component {
render() {
return (
<ChildC />
<ChildC />
)
}
}
class ChildC extends React.Component {
render() {
return (
<h1>
Child Component
</h1>
)
}
}
Теперь выполним вложение:
return (
<ChildC />
<ChildC />
)
в React.Fragment
. Получаем такой результат:
class ParentC extends React.Component {
render() {
return (
<React.Fragment>
<ChildC />
<ChildC />
</React.Fragment>
)
}
}
Перейдем ко второму примеру:
class ParentC extends React.Component {
render() {
return (
<div>
<ChildComponent />
<ChildComponent />
</div>
)
}
}
class ChildComponent extends React.Component {
render() {
return (
<div>
<h1>Hello Child Component</h1>
<h1>Hello Child Component</h1>
</div>
)
}
}
Удаляем теги div
и добавляем React.Fragment
вместо них:
class ParentC extends React.Component {
render() {
return (
<React.Fragment>
<ChildComponent />
<ChildComponent />
</React.Fragment>
)
}
}
class ChildComponent extends React.Component {
render() {
return (
<React.Fragment>
<h1>Hello Child Component</h1>
<h1>Hello Child Component</h1>
</React.Fragment>
)
}
}
Переходим к третьему примеру и заменяем дополнительные теги div в BodyComponent и HeaderComponent на React.Fragment:
class HeaderComponent extends React.Component {
render() {
return (
<div>
<th>Name</th>
<th>Occupation</th>
<th>Age</th>
</div>
)
}
}
class BodyComponent extends React.Component {
render() {
return (
<div>
<td>Chidume Nnamdi</td>
<td>Software Dev</td>
<td>27</td>
</div>
)
}
}
|
|
|
v
class HeaderComponent extends React.Component {
render() {
return (
<React.Fragment>
<th>Name</th>
<th>Occupation</th>
<th>Age</th>
</React.Fragment>
)
}
}
class BodyComponent extends React.Component {
render() {
return (
<React.Fragment>
<td>Chidume Nnamdi</td>
<td>Software Dev</td>
<td>27</td>
</React.Fragment>
)
}
}
Таблица будет отображена следующим образом:
<table>
<tr>
<th>Name</th>
<th>Occupation</th>
<th>Age</th>
</tr>
<tr>
<td>Chidume Nnamdi</td>
<td>Software Dev</td>
<td>27</td>
</tr>
</table>
Как мы и хотели, без лишних тегов div!!!
Если вам кажется, что многократное написание React.Fragment
занимает слишком много времени, то можно использовать сокращенный синтаксис. Он выглядит таким образом: <>...</>
. Теперь компоненты BodyComponent
и HeaderComponent
выглядят так:
class HeaderComponent extends React.Component {
render() {
return (
<React.Fragment>
<th>Name</th>
<th>Occupation</th>
<th>Age</th>
</React.Fragment>
)
}
}
class BodyComponent extends React.Component {
render() {
return (
<React.Fragment>
<td>Chidume Nnamdi</td>
<td>Software Dev</td>
<td>27</td>
</React.Fragment>
)
}
}
Можно записать и таким образом:
class HeaderComponent extends React.Component {
render() {
return (
<>
<th>Name</th>
<th>Occupation</th>
<th>Age</th>
</>
)
}
}
class BodyComponent extends React.Component {
render() {
return (
<>
<td>Chidume Nnamdi</td>
<td>Software Dev</td>
<td>27</td>
</>
)
}
}
Заключение
Мы рассмотрели проблему невозможности возврата нескольких элементов и способ ее решения с помощью React.Fragment, а также научились использовать сокращенный синтаксис React.Fragment.
Благодаря React.Fragment можно насладиться преимуществом возврата нескольких элементов без необходимости добавлять дополнительные теги или теги div, которые окружают элементы и загромождают DOM.
Перевод статьи Chidume Nnamdi: Understanding Fragments in React