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()
, так что контроллер может вызвать повторный рендеринг своего компонента-хоста.