React¶
Пакет @lit/react предоставляет утилиты для создания компонентов-оберток React для веб-компонентов и пользовательских хуков из reactive controllers.
Обертка для React-компонентов позволяет задавать свойства пользовательских элементов (вместо атрибутов), сопоставлять события DOM с обратными вызовами в стиле React, а также обеспечивает корректную проверку типов в JSX с помощью TypeScript.
Обертки предназначены для двух разных аудиторий:
- Пользователи веб-компонентов могут обертывать компоненты и контроллеры для использования в собственных React-проектах.
- Поставщики компонентов могут публиковать React-обертки, чтобы у пользователей React были идиоматические версии их компонентов.
Зачем нужны обертки?¶
React уже умеет рендерить веб-компоненты, поскольку пользовательские элементы - это просто HTML-элементы, а React знает, как рендерить HTML. Но React делает некоторые предположения об элементах HTML, которые не всегда справедливы для пользовательских элементов, и обрабатывает имена тегов в нижнем регистре иначе, чем имена компонентов в верхнем регистре, что может сделать использование пользовательских элементов сложнее, чем нужно.
Например, React предполагает, что все JSX-свойства соответствуют атрибутам HTML-элементов, и не предоставляет возможности установить свойства. Это затрудняет передачу сложных данных (например, объектов, массивов или функций) в веб-компоненты. React также предполагает, что все события DOM имеют соответствующие "свойства события" (onclick, onmousemove и т. д.), и использует их вместо вызова addEventListener(). Это означает, что для правильного использования более сложных веб-компонентов вам часто придется использовать ref() и императивный код. (Подробнее об ограничениях интеграции веб-компонентов в React см. в Custom Elements Everywhere).
React работает над исправлением этих проблем, а пока наши обертки позаботятся об установке свойств и прослушивании событий за вас.
Пакет @lit/react предоставляет два основных экспорта:
createComponent()создает компонент React, который обертывает существующий веб-компонент. Обертка позволяет вам устанавливать реквизиты компонента и добавлять к нему слушателей событий, как и к любому другому компоненту React.useController()позволяет использовать реактивный контроллер Lit в качестве крючка React.
createComponent¶
Функция createComponent() создает обертку React-компонента для пользовательского класса элемента. Обертка корректно передает React props свойствам, принимаемым пользовательским элементом, и прослушивает события, отправляемые пользовательским элементом.
Использование¶
Импортируйте React, класс пользовательского элемента и createComponent.
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Определив компонент React, вы можете использовать его так же, как и любой другой компонент React.
1 2 3 4 5 | |
Посмотрите его в действии в примерах React playground.
Опции¶
createComponent принимает объект options со следующими свойствами:
tagName: Имя тега пользовательского элемента.elementClass: Класс пользовательского элемента.react: Импортированный объектReact. Он используется для создания компонента-обертки с предоставленным пользователемReact. Это также может быть импортpreact-compat.events: Объект, сопоставляющий реквизит обработчика событий с именем события, вызванного пользовательским элементом.
Использование слотов¶
Дети компонента, созданного с помощью createComponent(), будут отображаться в слоте по умолчанию пользовательского элемента.
1 2 3 | |
Чтобы переместить дочерний элемент в определенный слот, можно добавить стандартный атрибут slot.
1 2 3 4 5 | |
Поскольку компоненты React сами по себе не являются элементами HTML, они обычно не могут напрямую иметь атрибут slot. Для рендеринга в именованный слот компонент должен быть обернут элементом-контейнером с атрибутом slot. Если элемент обертки мешает стилизации, как, например, для макетов grid или flexbox, задайте ему стиль display: contents; (Подробности см. в MDN), чтобы исключить контейнер из рендеринга и рендерить только его дочерние элементы.
1 2 3 4 5 | |
Попробуйте это в примере React slots playground.
События¶
Опция events принимает объект, который сопоставляет имена реквизитов React с именами событий. Когда пользователь компонента передает реквизит callback с одним из имен реквизитов события, обертка добавит его в качестве обработчика соответствующего события.
Хотя имя реквизита React может быть любым, рекомендуемое соглашение - добавлять on перед именем события. Это соответствует тому, как React планирует реализовать поддержку событий для пользовательских элементов. Вы также должны убедиться, что это имя реквизита не пересекается с какими-либо существующими свойствами элемента.
В TypeScript тип события можно указать, приведя имя события к полезному типу EventName. Это хорошая практика для того, чтобы пользователи React получали наиболее точные типы для своих обратных вызовов событий.
Тип EventName - это строка, которая принимает интерфейс события в качестве параметра типа. Здесь мы приводим имя 'my-event' к типу EventName<MyEvent>, чтобы обеспечить правильный тип события:
1 2 3 4 5 6 7 8 9 10 11 12 | |
Приведение имени события к EventName<MyEvent> приводит к тому, что у компонента React появляется свойство обратного вызова onMyEvent, принимающее параметр MyEvent вместо простого Event:
1 2 3 4 5 | |
Как это работает¶
Во время рендеринга обертка получает реквизиты от React и, основываясь на опциях и классе пользовательского элемента, изменяет поведение некоторых реквизитов:
- Если имя реквизита является свойством пользовательского элемента, как определено с помощью проверки
in, обертка устанавливает это свойство элемента в значение реквизита. - Если имя реквизита - это имя события, переданное в опцию
events, значение реквизита передается вaddEventListener()с именем события. - В противном случае свойство передается в функцию React
createElement()для отображения в качестве атрибута.
Как свойства, так и события добавляются в обратных вызовах componentDidMount() и componentDidUpdate(), поскольку для доступа к элементу он должен быть уже инстанцирован React.
Для событий createComponent() принимает отображение имен реквизитов React событий на события, запускаемые пользовательским элементом. Например, передача {onfoo: 'foo'} означает, что функция, переданная через prop с именем onfoo, будет вызвана, когда пользовательский элемент вызовет событие foo с этим событием в качестве аргумента.
useController¶
Реактивные контроллеры позволяют разработчикам подключаться к жизненному циклу компонента, чтобы связать воедино состояние и поведение, связанные с функцией. Они похожи на хуки React по пользовательским кейсам и возможностям, но являются обычными объектами JavaScript вместо функций со скрытым состоянием.
useController() позволяет создавать React-хуки из реактивных контроллеров, что обеспечивает совместное использование состояния и поведения веб-компонентов и React.
Использование¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
Смотрите пример контроллера мыши в документации по реактивным контроллерам для его реализации.
Как это работает¶
useController() создает пользовательский хост-объект для переданного ему контроллера и управляет жизненным циклом контроллера с помощью хуков React.
useState()используется для хранения экземпляра контроллера иReactControllerHost.- Тело хука и обратные вызовы
useLayoutEffect()максимально близко эмулируют жизненный циклReactiveElement. ReactControllerHostреализуетaddController(), чтобы композиция контроллеров работала и жизненные циклы вложенных контроллеров вызывались корректно.ReactControllerHostтакже реализуетrequestUpdate(), вызывая сеттерuseState(), так что контроллер может вызвать повторный рендеринг своего компонента-хоста.