При работе с 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 NnamdiUnderstanding Fragments in React

Предыдущая статьяОвладей Python, создавая реальные приложения. Часть 7
Следующая статьяКакие языки программирования лучше учить в 2019 году