React v16.2.0: улучшенная поддержка фрагментов

Ноябрь 28, 2017 Клемент Хонг


React 16.2 теперь доступен! Самым большим дополнением является улучшенная поддержка возврата нескольких дочерних элементов из метода отрисовки(рендеринга) компонента. Мы называем эту возможность фрагментами.

Фрагменты выглядят как пустые теги JSX. Они позволяют группировать список дочерних элементов без добавления дополнительных узлов в DOM:


Код
    
  render() {
    return (
      <>
        <ChildA />
        <ChildB />
        <ChildC />
      </>
    );
  }
  

Эта потрясающая новая функция стала возможной благодаря дополнениям как к React, так и к JSX.


Что такое фрагменты?

Общим примером является компонент, который возвращает список дочерних элементов. Рассмотрим данный пример HTML:


Код
    
  Какой-то текст.
  <h2>Первый заголовок</h2>
  Еще текст.
  <h2>Второй заголовок</h2>
  Еще больше текста.
  
  

До версии 16 единственным способом достичь этого в React было обертывание потомков в дополнительный элемент, обычно div или span:


Код
    
  render() {
    return (
      // Дополнительный элемент div :(
      <div>
        Какой-то текст.
        <h2>Первый заголовок </h2>
        Еще текст.
        <h2>Второй заголовок </h2>
        Еще больше текста.
      </div>
    );
  }
  

Чтобы устранить это ограничение, в React 16.0 была добавлена поддержка возврата массива элементов из метода отрисовки компонента. Вместо того, чтобы обертывать дочерние элементы в элемент DOM, вы можете поместить их в массив:


Код
    
  render() {
     return [
      "Какой-то текст.",
      <h2 key="heading-1">Первый заголовок</h2>,
      "Еще текст.",
      <h2 key="heading-2">Второй заголовок</h2>,
      "Еще больше текста."
     ];
  }
  

Однако такой подход имеет некоторые значительные отличия от обычного JSX:

  • Потомки в массиве должны быть разделены запятыми.
  • У потомков в массиве должен быть ключ, предотвращающий React предупреждения в консоли.
  • Строки должны быть заключены в кавычки.

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


Код
    
    render() {
      return (
        <Fragment>
          Какой-то текст.
          <h2>Первый заголовок</h2>
          Еще текст.
          <h2>Второй заголовок</h2>
          Еще больше текста.
        </Fragment>
      );
    }
  

Вы можете использовать <Fragment/> так же, как используете любой другой элемент, не изменяя способ написания JSX. Никаких запятых, никаких ключей, никаких кавычек.

Компонент Fragment доступен в главном объекте React:


Код
    
  const Fragment = React.Fragment;
  
  <Fragment>
    <ChildA />
    <ChildB />
    <ChildC />
  </Fragment>
  
  // Так тоже работает
  <React.Fragment>
    <ChildA />
    <ChildB />
    <ChildC />
  </React.Fragment>
  


JSX синтаксис фрагментов

Фрагменты - это общий паттерн в нашей кодовой базе в Facebook. Мы ожидаем, что они будут широко приняты другими командами. Чтобы сделать опыт разработки максимально удобным, мы добавляем синтаксическую поддержку для фрагментов в JSX:


Код
    
  render() {
    return (
      <>
        Какой-то текст.
        <h2>Первый заголовок</h2>
        Еще текст.
        <h2>Второй заголовок</h2>
        Еще больше текста.
      </>
    );
  }
  

В React это является синтаксическим сахаром для элемента <React.Fragment/> из примера в предыдущем разделе. (Не-React фреймворки, которые используют JSX, могут компилировать во что-то другое).

Синтаксис фрагментов в JSX был вдохновлен изящным предшественником, таким как конструктор XMLList() <> </> в E4X. Использование пары пустых тегов предназначено для того, чтобы обозначить, что в DOM не будет добавлено реального элемента.

Фрагменты с ключами

Обратите внимание, что синтаксис <> </> не принимает атрибуты, включая ключи. Если вам нужен фрагмент с ключом, вы можете напрямую использовать <Fragment/>. Вариант использования для этого - маппинг коллекции в массив фрагментов - например, для создания списка описания:


Код
    
  function Glossary(props) {
    return (
      <dl>
        {props.items.map(item => (
          // Без ключа `key`, React выдаст предупреждение в консоль
          <Fragment key={item.id}>
            <dt>{item.term}</dt>
            <dd>{item.description}</dd>
          </Fragment>
        ))}
      </dl>
    );
  }
  

key - это единственный атрибут, который может быть передан Fragment. В будущем мы можем добавить поддержку дополнительных атрибутов, таких как обработчики событий.

Демо

Вы можете поэкспериментировать с JSX синтаксисом фрагментов с помощью данного CodePen .


Поддержка синтаксиса фрагментов

Поддержка синтаксиса фрагментов в JSX будет зависеть от инструментов, которые вы используете для построения своего приложения. Пожалуйста, будьте терпеливы, поскольку сообщество JSX работает над принятием нового синтаксиса. Мы тесно сотрудничаем с разработчиками самых популярных проектов:

Create React App

Экспериментальная поддержка синтаксиса фрагментов будет добавлена в приложение Create React. Устойчивый выпуск может занять немного больше времени, так как мы ожидаем одобрения другими проектами.

Babel

Поддержка фрагментов JSX доступна в Babel v7.0.0-beta и выше! Если вы уже на Babel 7, просто обновите последнюю версию Babel и плагина преобразования:


Код
    
  # для пользователей yarn
  yarn upgrade @babel/core @babel/plugin-transform-react-jsx
  # для пользователей npm
  npm update @babel/core @babel/plugin-transform-react-jsx
  

Или, если вы используете react-present :


Код
    
  # для пользователей yarn
  yarn upgrade @babel/core @babel/preset-react
  # для пользователей npm
  npm update @babel/core @babel/preset-react
  

Обратите внимание, что Babel 7 технически все еще в бета-версии, но стабильный релиз уже скоро.

К сожалению отсутствует поддержка для Babel 6.x, и в настоящее время нет планов по обратной совместимости.

Babel с Webpack

Если вы используете Babel с Webpack, дополнительных шагов не требуется, потому что babel-loader будет использовать установленную вами версию Babel.

Babel c другими фреймворками

Если вы используете JSX с не-React фреймворком, например Inferno или Preact, есть опция pragma, доступная в babel-plugin-transform-react-jsx, которая настраивает компилятор Babel для работы с синтаксисом <> </>.

TypeScript

TypeScript имеет полную поддержку синтаксиса фрагментов! Обновитесь до версии 2.6.2. (Обратите внимание, что это важно, даже если вы уже на версии 2.6.1, так как поддержка была добавлена как релиз патча в 2.6.2.)

Проведите апгрейд до последнего TypeScript с помощью команды:


Код
    
  # для пользователей yarn
  yarn upgrade typescript
  # для пользователей npm
  npm update typescript
  

Flow

Flow поддерживает фрагменты JSX начиная с версии 0.59 ! Просто запустите:


Код
    
  # для пользователей yarn
  yarn upgrade flow-bin
  # для пользователей npm
  npm update flow-bin
  

чтобы обновить Flow до последней версии.

Prettier

Prettier добавляет поддержку фрагментов в своем 19 релизе .

ESLint

JSX фрагменты поддерживаются ESLint 3.x, когда он используются вместе с babel-eslint :


Код
    
  # для пользователей yarn
  yarn add [email protected] [email protected]
  # для пользователей npm
  npm install [email protected] [email protected]
  

или если он у вас уже есть, то сделайте апгрейд:


Код
    
  # для пользователей yarn
  yarn upgrade [email protected] [email protected]
  # для пользователей npm
  npm update [email protected] [email protected]
  

Убедитесь, что у вас есть следующая строка внутри вашего файла конфигурации .eslintrc:


Код
    
  "parser": "babel-eslint"
  

Это всё!

Обратите внимание, что babel-eslint официально не поддерживается ESLint. Мы рассмотрим возможность добавления фрагментов к ESLint 4.x в ближайшие недели (см. issue #9662 ).


Поддержка редактором

Может потребоваться некоторое время для поддержки синтаксиса фрагментов в текстовом редакторе. Будьте терпеливы, поскольку сообщество работает над принятием последних изменений. Тем временем вы можете увидеть ошибки или некорректную подсветку, если ваш редактор еще не поддерживает синтаксис фрагментов. Как правило, эти ошибки можно безопасно игнорировать.

Поддержка синтаксиса TypeScript редактором

Если вы пользователь TypeScript - отличная новость! Поддержка редактором фрагментов JSX уже доступна в Visual Studio 2015, Visual Studio 2017, Visual Studio Code и Sublime Text с помощью функции Package Control.


Другие инструменты

Для других инструментов ознакомьтесь с соответствующей документацией, чтобы проверить наличие поддержки. Однако, если вы заблокированы своей инструментальной базой, вы всегда можете начать с использования компонента <Fragment> и позже выполнить codemod, чтобы заменить его сокращенным синтаксисом, когда соответствующая поддержка будет доступна.


Установка

React v16.2.0 доступен в реестре npm.

Чтобы установить React 16, запустите:


Код
    
  # для пользователей yarn
  yarn add [email protected]^16.2.0 [email protected]^16.2.0
  # для пользователей npm
  npm install --save [email protected]^16.2.0 [email protected]^16.2.0
  

Мы также предоставляем сборки UMD React-а через CDN:


Код
    
  <script crossorigin src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
  <script crossorigin src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
  

Обратитесь к документации для подробных инструкций по установке .


Лог изменений

React

  • Добавление Fragment как именованный export в React. ( @clemmy в #10783 )
  • Поддержка экспериментальных типов Call/Return в утилитах React.Children. ( @MatteoVH в #11422 )

React-DOM

  • Исправление радиокнопок, которые не выбираются при использовании нескольких списков радиокнопок. ( @landvibe в #11227 )
  • Исправление радиокнопок, не принимающих событие onChange в некоторых случаях. ( @jquense в #11028 )

React Test Renderer

  • Исправлен баг слишком раннего срабатывания коллбэка метода setState(), когда он вызывается из метода componentWillMount. ( @accordeiro в #11507 )

React Reconciler

  • Представлен response-reconciler/reflection с утилитами, полезными для кастомной отрисовки. ( @rivenhk в #11683 )

Внутренние изменения

Многие тесты были переписаны в отличие от публичного API. Большое спасибо всем, кто внес свой вклад!


Благодарности

Этот выпуск стал возможным благодаря нашим open source участникам. Большое спасибо всем, кто создавал issue, внес свой вклад в обсуждениях синтаксиса, проверял PR-ы, добавил поддержку фрагментов JSX в сторонние библиотеки и многое другое!

Особая благодарность командам TypeScript и Flow, а также команде сопровождения Babel, которые помогли реализовать инструментальную поддержку нового синтаксиса.

Благодарим Gajus Kuizinas и других участников, которые прототипировали компонент Fragment в open source.