React Native: создание нативного компонента

При разработке приложения React Native мы большую часть времени работаем только со стороной JavaScript и позволяем нативным материалам либо самому React Native, либо внешним библиотекам. Однако в некоторых случаях полезно или даже необходимо написать нативный компонент (то есть такой, который частично состоит из фактического кода Android (Java) и IOS (Objective-C/swift)). В этом уроке мы рассмотрим создание такого нативного компонента. Сначала просто очень простой текстовый компонент, а затем расширяем его, пока не дойдем до простого компонента кнопки/ссылки.

Теперь нативный компонент состоит, помимо нативной части, еще и из бита JavaScript, который определяет интерфейс для него и может также содержать логику. Две части взаимодействуют через мост, управляемый React Native, который обычно используется для отправки и прослушивания событий.

Прежде чем мы начнем, нам нужно решить, либо поместить код в существующий проект, либо создать отдельный проект только для нативного компонента, от которого мы затем зависим. Лично я предпочитаю последний вариант, так как он позволяет не связываться с нативным кодом полного проекта React Native и позволяет легко опубликовать компонент, если вы захотите это сделать в какой-то момент.

Для создания такого проекта мне пригодился этот инструмент: https://github.com/frostney/react-native-create-library
Он помогает настроить новые библиотеки React Native (настройка структуры проектов и базовая конфигурация). Просто следуя инструкциям в файле Readme проекта, мы устанавливаем инструмент cli глобально (либо с помощью npm, либо с помощью yarn), а затем мы можем создать проект:

Одна из вещей, которую вы можете заметить, это наличие папки Windows для разработки Windows Phone. Поскольку мы не собираемся внедрять версию для Windows Phone, мы можем удалить эту папку. Кроме того, просмотрев файл package.json, мы хотим обновить родную одноранговую зависимость React до новейшей (или, по крайней мере, более новой) версии.

1 — очень простой текстовый компонент

Теперь, когда базовая настройка завершена, мы можем начать с Android с более конкретной настройкой 😉

Нам нужно настроить build.gradle в папке Android, так как в нем не хватает нескольких вещей. Нам нужно добавить зависимость сборки, чтобы убедиться, что подключаемый модуль com.android.library доступен, и добавить репозиторий jCenter.

Теперь, когда это сделано, просматривая исходный каталог Android до конца, мы находим два класса — RNMyTextModule, который нам пока не нужен, поэтому мы можем просто удалить его, и RNMyTextPackage, который содержит все модули для этого. библиотека (модули js, модули просмотра, нативные модули). Здесь мы должны удалить RNMyTextModule из списка нативных модулей. На самом деле мы хотим создать модуль представления. Такой модуль обычно создается с помощью диспетчера представлений, который React Native уже предоставляет диапазон, из которого мы можем расшириться. В этом примере мы будем использовать SimpleViewManager. Помимо диспетчера представлений нам также понадобится само представление. В этом случае мы используем существующий компонент Android TextView. Ниже приведен код диспетчера представлений:

Мы расширяемся от SimpleViewManager, который уже делает за нас много работы, например, управляет теневым узлом. Все, что мы на самом деле делаем, — это создаем TextView в createViewInstance и устанавливаем текст представлений через свойство text.

Теперь давайте посмотрим на iOS. В папке у нас есть только файл проекта XCode, который мы можем использовать для открытия проекта, и один файл кода, представляющий нативный модуль. Мы снова хотим создать менеджер представлений для текстового компонента, как в версии для Android. Вот реализация:

RNMyText.h

RNMyText.m

Сначала мы наследуем от RCTViewManager, снова предоставляя нам важную функциональность для управления представлением. В реализации у нас есть функция view, которая возвращает новый UILabel, и у нас есть свойство для установки текста в представлении. В этом случае макрос RCT_EXPORT_VIEW_PROPERTY установит текстовое свойство UILabel с заданным значением. Если свойство React не соответствует имени свойства компонента iOS или требуется особая обработка, мы можем использовать RCT_CUSTOM_VIEW_PROPERTY. Например, мы могли бы добиться того же (установив свойство text) следующим образом:

Последнее, что нам нужно, — это JavaScript, который загружает соответствующий нативный модуль. Если мы посмотрим в index.js, у нас уже есть код для загрузки нативного модуля, однако мы собираемся сделать это немного по-другому. Основное отличие состоит в том, что мы используем requireNativeComponent для загрузки нативного компонента, в качестве первого параметра мы передаем имя, а вторым — объект, который определяет propTypes и, в идеале, имя.

Одна вещь, которая обычно является хорошей практикой, — это обернуть этот собственный компонент в реагирующий компонент. Однако для простоты мы остановимся на этом.

Далее нам просто нужно использовать компонент в проекте React Native. Мы можем либо создать его с помощью react-native init, либо использовать существующий. Затем нам нужно установить модуль узла для нашей библиотеки и слинковать его (это соответственно установит все нативные биты).

Добавьте это в зависимости package.json (вам нужно будет настроить путь):

"react-native-my-text": "file:///Path/To/ProjectFolder"

Затем выполните эти команды:

yarn install 
react-native link

Теперь в проекте у нас должна быть возможность использовать наш нативный компонент.

И теперь это должно отображать текст (похоже, что backgroundColor по умолчанию имеет черный цвет в iOS, поэтому мы явно установили его прозрачным).

2 — простой текстовый компонент с дополнительным стилем

Затем мы можем захотеть добавить некоторые дополнительные стили — как видите, нам уже повезло, потому что мы получаем все существующие стили View бесплатно. Однако, скажем, мы хотим установить fontSize через свойство стиля. Для этого нам нужно реализовать собственный теневой узел в коде Android и iOS.
В Android мы переключаемся в нашем классе RNMyTextViewManager с SimpleViewManager на BaseViewManager, что позволяет нам предоставлять собственный теневой узел. Затем мы создаем класс для нашего теневого узла, который получает fontSize со стороны JS и добавляет его в качестве данных для обновления представления. Затем в методе updateExtraData мы фактически выполняем обновление.

В iOS нам не нужно менять класс, от которого мы наследуемся, мы можем просто переопределить функцию, в которой мы возвращаем наш теневой узел, и далее есть макрос, аналогичный макросу для представлений для управления свойствами, который мы используем для размер шрифта. Затем в теневом узле у нас есть функция обновления, с помощью которой мы можем обновить fontSize представления, аналогично тому, как мы это делаем в версии для Android.

Для стороны JS нам нужно добавить fontSize к нашим propTypes в качестве стиля. Чтобы сохранить propTypes компонента View, нам нужно добавить ViewStylePropTypes. Наконец, нам нужно убедиться, что мы поместили его после View.proptypes, так как в противном случае это переопределит наш пользовательский стиль propType.

Теперь мы можем использовать fontSize в качестве стиля в нашем компоненте.

3 — текстовый компонент с дополнительным стилем и обработкой событий

Хорошо, последнее, что мы рассмотрим, — это реализация поведения onTouch, которое запускает событие при касании текста. Обычно для этого мы использовали бы такой компонент, как TouchableHighlight, но для этого урока мы реализуем его непосредственно в компоненте.

Снова начнем с Android. Там мы можем переопределить функцию addEventEmitters в нашем диспетчере представлений. В функции мы прослушиваем события касания для TextView, а в обработчике событий мы используем диспетчер событий из React Native для отправки события на сторону JS. В качестве данных события мы отправляем координаты x и y касания. Еще нам нужно переопределить getExportedCustomBubblingEventTypeConstants, который возвращает карту, определяющую события, отправляемые на сторону JS. (Если вам нужны не всплывающие события, для этого есть getExportedCustomDirectEventTypeConstants).

На стороне iOS мы используем свойство представления для вызова onTouch, которое передается представлению. Теперь в этом случае мы реализуем наш собственный класс меток, наследуемый от UILabel, и там мы реализуем UITapGestureRecognizer, который мы используем для регистрации сенсорного прослушивателя, и там вызываем функцию onTouch и снова передаем координаты х/у.

На стороне JS мы хотим расширить наши propTypes и добавить туда функцию onTouch.

И самое последнее — прослушивание событий onTouch в компоненте.

И это все для этого урока. Есть много других вещей, которые мы можем сделать при реализации нативных компонентов для React Native, но этого должно быть достаточно для многих случаев использования. Полный исходный код можно найти здесь: https://github.com/reneweb/react-native-component-tutorial

Первоначально опубликовано на rwlive.wordpress.com 6 апреля 2017 г.