React Fragments: Обзор

Published on
Authors

React Fragments — это простая, но элегантная функция, которая была добавлена React v16.2.0. Простое понимание этого позволит вам писать более качественные компоненты React и сэкономить массу времени при создании и стилизации макетов.

Это руководство поможет вам понять React Fragments и различные варианты его использования.

Вот что мы расскажем:

  • Что такое React Fragments?
  • Fragments в шблонах React
  • Отрисовка списков с помощью React Fragments

Что такое React Fragments?

Fragments — это современный синтаксис для добавления нескольких элементов в компонент React без помещения их в дополнительный DOM узел.

React Fragments не создают никаких дополнительных элементов в DOM, а это означает, что дочерние компоненты фрагмента будут отображаться без какого-либо узла DOM-оболочки. React Fragments позволяют сгруппировать несколько родственных компонентов без добавления ненужной разметки в HTML.

Fragments в React шаблонах

React Fragments служат более чистой альтернативой использованию ненужных div в вашем коде.

Рассмотрим следующий пример, чтобы создать простой макет строк и столбцов с помощью Flexbox:

import "./styles.css";

const Row = ({ children }) => <div className="row">{children}</div>;

const Column = ({ children }) => <div className="col">{children}</div>;

const Box = ({ color }) => (
  <div className="box" style={{ backgroundColor: color }}></div>
);

export default function App() {
  return (
    <Row>
      <Column>
        <Box color="#007bff" />
      </Column>
      <Column>
        <Box color="#fc3" />
      </Column>
      <Column>
        <Box color="#ff3333" />
      </Column>
    </Row>
  );
}

Каждая Row отображает div с заключенным содержимым, выровненным в одну строку, а Column визуализирует закрывающее содержимое по вертикали. Внутри каждого Column есть компонент Box, который отображает простой div с контейнером фиксированной ширины и цветом фона, переданным ему в качестве свойств(props).

/* styles.css */
.row {
  display: flex;
}
.col {
  flex: 1;
}
.box {
  min-width: 100px;
  min-height: 100px;
}

Давайте проведем рефакторинг приведенного выше кода, чтобы разделить первые два столбца в другом компоненте под названием ChildComponent. Представьте, что это переиспользуемый компонент, который вы, возможно, захотите вынести в отдельный файл.

export default function App() {
  return (
    <Row>
      <ChildComponent />
      <Column>
        <Box color="#ff3333" />
      </Column>
    </Row>
  );
}

const ChildComponent = () => (
  <div>
    <Column>
      <Box color="#007bff" />
    </Column>
    <Column>
      <Box color="#fc3" />
    </Column>
  </div>
);

Ожидаемый результат должен быть таким же, как и раньше, но это не так. Разделение первых двух столбцов в отдельный компонент ChildComponent нарушает макет:

В ChildCompoent есть div, обертывающий все его элементы JSX, чтобы сгруппировать их вместе. Однако этот дополнительный div вызывает разрыв макета, поскольку браузер считает его как часть макета. Ваш браузер не знает, что вы добавили этот div, чтобы избежать ошибки, и он используется просто как оболочка для вашего включающего HTML.

По мере того как дерево компонентов погружается глубже, становится трудно отладить и отследить, откуда берутся дополнительные узлы. Точно так же, если вы используете CSS-сетки для стилизации и разработки макетов, ненужные блоки divs могут привести к поломке макета. Простое решение — вместо этого обернуть JSX вашего компонента во React Fragment.

import React from 'react';
const ChildComponent = () => (
  <React.Fragment>
    <Column>
      <Box color="#007bff" />
    </Column>
    <Column>
      <Box color="#fc3" />
    </Column>
  </React.Fragment>
);

Есть много способов создавать и отображать Fragments. Вы можете создать фрагмент, используя свойство Fragment импортировав его из объекта React, как показано выше. Вы также можете импортировать Fragment из React как компонент React и использовать его аналогичным образом.

import React, {Fragment} from 'react';
const ChildComponent = () => (
  <Fragment>
    <Column>
      <Box color="#007bff" />
    </Column>
    <Column>
      <Box color="#fc3" />
    </Column>
  </Fragment>
);

Наконец, вы можете создать React Fragment на лету, используя сокращенный синтаксис, чтобы обернуть компоненты с помощью пустого элемента HTML. Это самый чистый и простой способ использования Fragments; напоминает использование обычного HTML-элемента.

const ChildComponent = () => (
  <>
    <Column>
      <Box color="#007bff" />
    </Column>
    <Column>
      <Box color="#fc3" />
    </Column>
  </>
);

Использование любого из трех вышеперечисленных вариантов возвращает оригинальный макет, поскольку он устраняет любой бессмысленный div в вашей DOM.

Отрисовка списков с помощью React Fragments

Давайте посмотрим на еще один распространенный вариант использования Fragments. Допустим, вы хотите отобразить список элементов на странице. Этот список может быть статическим, сгенерированным из локального файла JSON или полученным из API.

Для примера мы будем использовать статический список:

import React from 'react';
const items = ["Item 1", "Item 2", "Item 3"];
const ItemRenderer = ({ item }) => (
  <div>
    <p>Rendering item:</p>
    <p>{item}</p>
  </div>
);
const renderItem = () => items.map((item, index) =>
  <ItemRenderer key={index} item={item} />
);

Здесь вы просто обходите в цикле массив элементов и передаете каждый элемент в качестве props в компоненту ItemRenderer, которая отображает каждый отдельный элемент на странице. Если вы проверите приведенный выше код в браузере, у вас будет следующая структура DOM:

<div>
  <p>Rendering item:</p>
  <p>Item 1</p>
</div>
<div>
  <p>Rendering item:</p>
  <p>Item 2</p>
</div>
<div>
  <p>Rendering item:</p>
  <p>Item 3</p>
</div>

Каждый элемент отображается в родительском div, который не имеет значения как оболочка. Поскольку к включающему div не прикреплены стили или данные, его можно безопасно заменить на React Fragment, как показано ниже:

import React from 'react';
const items = ["Item 1", "Item 2", "Item 3"];
const ItemRenderer = ({ item }) => (
  <>
    <p>Rendering item:</p>
    <p>{item}</p>
  </>
);
const renderItem = () => items.map((item, index) =>
  <ItemRenderer key={index} item={item} />
);

Структура DOM теперь выглядит намного чище:

<p>Rendering item:</p>
  <p>Item 1</p>
  <p>Rendering item:</p>
  <p>Item 2</p>
  <p>Rendering item:</p>
  <p>Item 3</p>

Это очень упрощенный вариант использования, когда вы можете отображать дополнительный div в своей DOM. Чем больше ваши списки, тем значительнее влияние.

По мере того, как ваше приложение становится больше по размеру и становится сложнее по архитектуре, вы можете обнаружить, что отрисовываете значительное количество ненужных блоков div для визуализации больших списков в вашем приложении. Это может привести к раздуванию HTML-кода и снижению производительности на старых устройствах.

Это может быть совсем не так важно, но рендеринг ненужных HTML-элементов — всегда плохая практика. Если у вас есть общий компонент списка для вашего приложения, рассмотрите возможность использования Fragments в качестве оболочки, чтобы не абстрагироваться от чистого кода и семантики.

Наконец, фрагменты позволяют передавать key property в качестве props, но учтите, что это единственное свойство, которое оно может принять:

import React from 'react';
const items = ["Item 1", "Item 2", "Item 3"];
const ItemRenderer = () => (
  <React.Fragment>
    <h1>List of all items:</h1>
    {items.map((item, index) => (
      <React.Fragment key={index}>
        <p>Rendering Item:</p>
        <p>{item}</p>
      </React.Fragment>
    )}
  </React.Fragment>
);

Вы можете передавать props своему Fragment, только если вы делаете рендеринг, как показано выше. Props нельзя передавать во Fragments, если вы используете сокращенную запись (<></>). Хорошей практикой является использование синтаксиса React.Fragment или Fragment, когда вам нужно передать ключи, особенно при рендеринге с использованием цикла.

Заключение

Fragments позволяют писать более чистый, читаемый и поддерживаемый код. Они не заменяют div в вашем HTML, но они предлагают лучший подход к структурированию и рендерингу вашей разметки, если вы используете ненужные divв своем коде.

Вы можете избежать проблем, нарушающих ваши макеты, или потенциально оптимизировать время рендеринга разметки с помощью фрагментов. Однако вы должны использовать их только при необходимости. Если вам нужна оболочка для вашего JSX для стилизации, используйте вместо этого div.