Это первая часть серии из трех частей, в которой мы будем использовать React Native и Reanimated 2 для создания пользовательского слайдера, который сможет асинхронно загружать дочерние компоненты, предоставлять расширяемую библиотеку индикаторов прокрутки и предлагать большая гибкость для стилей, чем стандартные компоненты FlatList и ScrollView. Кроме того, мы рассмотрим стратегии оптимизации анимации и предотвращения ненужных повторных рендерингов.

Эта серия построена следующим образом:

  • Часть 1. Определение компонента и сборка слайдера
  • Часть 2: Создание типов индикаторов и подключение к слайдеру
  • Часть 3. Обсудите расширение ползунка и индикаторов и оптимизируйте

Наш последний компонент позволит нам сделать ползунки, подобные этим четырем:

Мечтаете обо всех слайд-шоу на тему видеоигр, которые можно сделать с помощью слайдера-сердечка? Хорошо, приступим!

Для более продвинутых программистов React я бы порекомендовал просто просмотреть оставшуюся часть этой части для контекста, а затем перейти к частям 2 и 3.

Зависимости и настройка

Во-первых, это зависимости для пакета, который мы будем создавать:

  • Реагировать на натив 0.63+
  • Expo SDK 41+ (не строгая зависимость, но мы будем использовать ее для простоты тестирования и обеспечения совместимости)
  • Реанимированный 2 (реаним-нативный-реанимированный) 2.2+
  • React Native Masked View (React-Native-Masked-View/Masked-View) 0.2+

Кроме того, мы установим react-native-safe-area-context для тестирования. Это не зависимость для нашего пакета.

Если вы выполнили глобальную установку Expo CLI, откройте терминал и перейдите в каталог, в котором вы хотите разместить этот проект. Затем запустите

expo init [project-name-you-like]

После инициализации нашего проекта перейдите к нему в терминале, и мы установим три пакета:

expo install react-native-reanimated @react-native-masked-view/masked-view react-native-safe-area-context

Наконец, нам нужно добавить реанимированный плагин в наш файл babel.config.js нашего проекта. Файл должен выглядеть примерно так, как показано ниже.

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

Дизайн

Мы хотим, чтобы наш компонент слайдера мог

  • Принимайте список любых типов компонентов, от базовых изображений/представлений React Native до более сложных или созданных пользователем компонентов, и загружайте их асинхронно в слайдере, подобном FlatList.
  • Принять индикатор прокрутки из набора, предоставленного пакетом или созданного пользователем
  • Примите индикатор передачи, который акцентирует движение между индикаторами прокрутки (как в ползунке сердца выше)
  • Обеспечьте возможность единообразного стиля для всех элементов прокрутки.

Для достижения этих целей мы примем следующие параметры для нашего компонента слайдера:

Наш файловый каталог будет выглядеть так

Ползунок компонентов

Определив поля, которые нам нужны, мы можем начать собирать наш слайдер. Обратите внимание, что мы не будем работать над индикаторами до части 2; сейчас мы просто собираемся сделать базовый компонент.

Для начала давайте отредактируем наш файл ComponentSlider.js. Мы будем использовать FlatList для обработки большей части функций прокрутки:

Нам нужно передать три важных реквизита для нашего FlatList, а именно data, renderItem и keyExtractor. data содержит все элементы прокрутки, необходимые FlatList для отображения. renderItem определяет способ отображения каждого элемента прокрутки. keyExtractor не является строго обязательным, однако это удобный реквизит, который позволяет нам определить функцию для создания уникальных идентификаторов для каждого элемента прокрутки.

Мы собираемся спроектировать наш слайдер таким образом, чтобы он не отвечал за изменение списка компонентов. То есть компонент-предок будет нести ответственность за управление мутациями в componentList. Все, что будет делать наш ползунок, — это перерисовывать, если в этом реквизите произойдут изменения.

Для этого нам нужно использовать хуки useState и useEffect. useState заставит ползунок перерисовываться каждый раз, когда вызывается setListData. useEffect гарантирует, что ползунок отражает новейший список компонентов. Добавляя componentList в качестве значения во второй аргумент useEffect, обратный вызов будет выполняться каждый раз, когда значение componentList изменяется (это важно — если вы не добавите componentList в массив во втором аргументом useEffect, компонент не будет работать должным образом, поскольку обратный вызов useEffect будет выполняться только при начальном рендеринге ползунка).

Далее создадим функцию renderItem. Все, что мы хотим сделать с каждым компонентом, переданным ползунку, — это обернуть его в представление и применить к нему стиль на уровне элемента прокрутки.

Продвинутые читатели: не волнуйтесь, мы сделаем mapComponent намного лучше в части 3, когда будем оптимизировать ;)

Обратите внимание, что keyExtractor будет искать свойство uid для каждого компонента, переданного в ползунок. Если таковой не найден, ключ будет равен индексу компонента в listData. Я также добавил реквизиты initialNumToRender и pagingEnabled для полноты картины. Ознакомьтесь с документацией FlatList, чтобы узнать, что делают эти реквизиты.

Тестирование, этап 1

Создав наш базовый слайдер, мы можем протестировать поведение нашего компонента. Давайте настроим наш App.js так, чтобы он содержал два компонента ползунка, один с прокруткой по горизонтали и один по вертикали, и три кнопки: одна добавляет элемент в список компонентов, одна удаляет последний элемент и третья заменяет все компоненты в списке компонентов. список.

Чтобы немного разбить этот код:

  • Стрелочная функция defaultComponent — это просто вспомогательная функция, которая упрощает создание экземпляра начального состояния, defaultComponentList, а также добавление или изменение переменной состояния componentList
  • componentIdentity позволяет мне отслеживать, сколько компонентов было создано, гарантируя, что я никогда не создам два дочерних компонента с одним и тем же ключом (если бы я просто использовал индекс компонента в массиве , я бы рискнул дублировать каждый раз, когда я удаляю, а затем добавляю элемент). useRef позволяет мне переносить значение между рендерингами, иначе оно будет сброшено или будет неопределенным.

Возвращаемый JSX создает наши ползунки и кнопки. Когда мы запустим вышеуказанное, мы получим следующее:

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

Для этого нам нужно вернуться к нашему ComponentSlider.

Обновленный ComponentSlider

Поведение по умолчанию, которое мы хотим, чтобы любые элементы прокрутки были размером с ползунок на экране. Легко, просто установите ширину и высоту каждого элемента прокрутки на 100%, верно?

Только один вопрос… 100% чего? Если вы внесете вышеуказанное изменение в свой код, вы быстро обнаружите, что ваши ползунки больше не скользят. Это связано с тем, что невозможно рассчитать 100% размера ползунка, поскольку ползунок не знает своего размера, пока не будут отрисованы все дочерние элементы. Задействована циклическая логика. Flex: 1 также не даст желаемого результата.

Как же тогда мы можем получить правильный размер? Мы не знаем размеров ползунка на экране до тех пор, пока он не будет отрендерен, так как же нам определить размеры элементов прокрутки?

Ответ заключается в событии View с именем onLayout, которое запускается сразу после расчета макета для FlatList. Мы можем взять ширину и высоту из этого события и сохранить их в переменной состояния, которую мы применим к каждому элементу прокрутки, чтобы получить правильную высоту и ширину:

Обязательно добавьте реквизит componentContainerStyle после ширины и высоты в mapComponent. В противном случае они переопределяют любые стили ширины и высоты, переданные в компонент.

После внесения вышеуказанных изменений у вас должно получиться следующее. Часть 1 завершена!

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