Главная / Статьи

Создание настраиваемого компонента выбора для React Native Paper

Пошаговое руководство по разработке и публикации react-native-paper-select в открытом доступе, охватывающее функции поиска, множественного выбора элементов, разделённые списки и аспекты сбалансирования производительности.

Издатель: REACTAPP.TOP12 мин чтения2663 слов

Источники: I Built a React Native Select Component — and Turned It Into an Open-Source Package: `react-native-paper-select`

Создание настоящего выпадающего списка в React Native почти никогда не бывает таким простым, как кажется

Если вы тратили время на попытки реализовать надлежащий выпадающий список в React Native, вы уже знаете, что «просто выпадающий список» быстро превращается в гораздо более сложную задачу.

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

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

Именно эту проблему несоответствия и было решено с помощью этого компонента.

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

Поэтому его разработали с нуля.

То, что изначально было довольно узкоспециализированным UI-компонентом, со временем превратилось в react-native-paper-select — open-source-пакет для React Native, включающий функции поиска, множественного выбора, отображения данных в виде чипов, разделенных списков, выбора с помощью радиокнопок, нижних панелей, меню, а также оптимизированный способ отрисовки списков.

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

Проблема

На первый взгляд выпадающие списки кажутся простыми.

Однако как только вы начинаете их реализовывать, возникает длинный список дизайнерских вопросов:

  • Как отображать текущее выбранное значение?
  • Требуется ли к полю возможность выбора одного или нескольких вариантов?
  • Какой формат наилучшим образом подходит для списка вариантов?
  • Как должна работать функция поиска внутри этого списка?
  • Должно ли поведение отличаться между Android и iOS?
  • Как отображать очень длинные списки, не снижая производительность?
  • Как сделать API, предназначенный для разработчиков, простым?
  • Как позволить настраивать компонент, не превращая его в что-то сложное в использовании?

Решать эти вопросы с нуля в каждом отдельном приложении не имело смысла.

Вместо того чтобы создавать ещё один уникальный компонент, предназначенный только для определённого приложения, имело больше смысла с самого начала спроектировать что-то, что можно было бы использовать повторно.

Основная идея была простой:

Создать компонент для выбора на основе собственных UI-примитивов React Native Paper и сделать его настолько удобным, чтобы другие разработчики React Native действительно захотели им пользоваться.

Представляем react-native-paper-select

react-native-paper-select — это настраиваемый компонент для React Native, поддерживающий как одиночный, так и множественный выбор.

Первая версия опиралась на уже существующие в React Native Paper знакомые элементы, включая:

  • TextInput
  • Checkbox
  • Dialog

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

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

Установка пакета

Чтобы начать работу, достаточно выполнить всего несколько шагов.

npm install react-native-paper-select

Или, если вы предпочитаете Yarn:

yarn add react-native-paper-select

После установки импортируйте компонент следующим образом:

import { PaperSelect } from 'react-native-paper-select';

Простой элемент выбора

Чтобы создать минимальное поле выбора, вам нужен лишь список опций и функция для обработки выбора.

Вот пример:

const [selected, setSelected] = useState({
  value: '',
  label: '',
});
const options = [
  {
    _id: '1',
    value: 'React Native',
  },
  {
    _id: '2',
    value: 'Flutter',
  },
  {
    _id: '3',
    value: 'Native Android',
  },
];

А затем отрисовка компонента:

<PaperSelect
  label="Framework"
  value={selected.label}
  onSelection={(value) => setSelected(value)}
  arrayList={options}
  selectedArrayList={selected}
/>

Здесь важно не то, насколько мало кода требуется.

Важно то, что скрывается за этим кодом.

С точки зрения приложения на самом деле существует всего два вопроса, на которые нужно ответить:

Какие опции доступны?

и

Что выбрал пользователь?

Все, что связано с отображением и взаимодействием, обрабатывается внутри самого компонента.

Одиночный выбор против множественного выбора

Возможность одновременно поддерживать одиночный и множественный выбор с самого начала была одним из ключевых требований к этому компоненту.

Одиночный выбор довольно прост в понимании:

Select Framework
----------------
○ React Native
○ Flutter
○ Native Android

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

Select Technologies
-------------------
☑ React Native
☑ TypeScript
☐ Flutter
☑ Node.js

Это означает, что компонент должен заботиться о следующем:

  1. Отслеживание состояния выбора
  2. Обработка снятия выбора
  3. Отображение нескольких выбранных значений одновременно
  4. Возврат обновленного набора выбранных элементов
  • Синхронизация отображаемого интерфейса с базовыми данными
  • Цель заключалась в том, чтобы всю эту сложность оставить внутри самого компонента, а не перекладывать её на каждого разработчика, использующего пакет.

    Почему React Native Paper?

    Поскольку существующее приложение уже опиралось на React Native Paper, создание компонента select на основе той же системы дизайна было естественным выбором.

    Вместо того чтобы заново создавать каждый визуальный элемент, компонент мог использовать готовые элементы интерфейса от Paper.

    В общих чертах внутренняя структура выглядела примерно так:

                    PaperSelect
                         │
              ┌──────────┴──────────┐
              │                     │
          TextInput            Selection UI
                                    │
                        ┌───────────┼───────────┐
                        │           │           │
                     Checkbox      Radio       List
    

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

    Любое приложение, уже использующее React Native Paper, может просто добавить этот компонент select, не внося стили, которые конфликтовали бы с остальной частью интерфейса.

    В первой версии компонента существовал только диалоговое окно для выбора.

    Интеракция работала следующим образом:

    TextInput
        ↓
    User taps field
        ↓
    Dialog opens
        ↓
    User selects an item
        ↓
    Selected value appears in TextInput
    

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

    Требования к пользовательскому интерфейсу сильно различаются в зависимости от приложения.

    Чтобы учесть это, компонент был доработан так, чтобы поддерживать несколько режимов отображения:

    • Диалоговое окно
    • Нижняя панель
    • Меню

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

    Например:

    <PaperSelect
      presentationStyle="bottomSheet"
      ...
    />
    

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

    Логика отбора остается неизменной независимо от выбранного стиля — меняется лишь способ её отображения пользователю.

    Работа с большими списками

    Менее очевидной проблемой у компонентов в стиле выпадающего списка является качество работы списка по мере его увеличения.

    Представьте компонент выбора с:

    10 items
    

    Его отрисовка — дело тривиальное.

    Теперь представьте компонент с:

    1,000 items
    

    или даже:

    10,000 items
    

    Отрисовка столь большого количества элементов списка одновременно может начать снижать отзывчивость мобильного приложения.

    Чтобы избежать этого, пакет использует FlashList для отрисовки своих списков опций.

    Концепция проста:

    Large dataset
          ↓
    Virtualized list
          ↓
    Render what is needed
          ↓
    Better scrolling performance
    

    Производительность — это проблема, которую нельзя откладывать до момента создания чего-то крупномасштабного.

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

    Поиск

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

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

    Последовательность действий выглядит следующим образом:

    Search
      ↓
    Filter options
      ↓
    Select result
      ↓
    Update selection
    

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

    Множественный выбор с индикаторами

    Отображение нескольких выбранных значений одновременно быстро приводит к перегрузке интерфейса.

    Например, такое значение:

    React Native, TypeScript, Node.js, MongoDB
    

    трудно интерпретировать, когда оно помещается в стандартное текстовое поле ввода.

    Индикаторы решают эту проблему.

    Выбранные значения могут отображаться в виде отдельных, различных блоков:

    [ React Native ] [ TypeScript ]
    [ Node.js ]      [ MongoDB ]
    

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

    Это также делает удаление отдельных элементов выбора гораздо более интуитивным.

    Секционные списки

    Еще одним требованием стала возможность группировки связанных опций вместе.

    Вместо одного длинного, плоского списка вида:

    React
    Angular
    Vue
    Node.js
    Java
    Python
    MongoDB
    PostgreSQL
    

    опции можно организовать под заголовками:

    Frontend
    
    ☐ React
    ☐ Angular
    ☐ Vue
    Backend☐ Node.js
    ☐ Java
    ☐ Python
    Database☐ MongoDB
    ☐ PostgreSQL
    

    Такая группировка значительно упрощает просмотр и понимание больших наборов опций.

    В режиме множественного выбора возможность сразу выбирать целую секцию значительно ускоряет работу.

    Например:

    ☑ Frontend
    
        ☑ React
        ☑ Angular
        ☑ Vue
    

    Это лучше, чем заставлять пользователя нажимать на каждый элемент по отдельности.

    Ползунки выбора для одиночного выбора

    Поле с галочками уместно тогда, когда можно выбрать более одного варианта.

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

    Именно поэтому пакет также предлагает вариант интерфейса выбора с использованием ползунков.

    Основная идея остается неизменной:

    Options
       ↓
    User chooses one
       ↓
    Selection state updated
    

    Изменяется только визуальное представление.

    Это подчеркивает важный принцип создания повторно используемых компонентов:

    Разделяйте логику выбора и способ её отображения.

    TypeScript и проектирование API

    Поскольку этот инструмент предназначен для использования другими разработчиками, форма API имеет столь же большое значение, как и внешний вид интерфейса.

    Человек, использующий пакет, не должен знать ничего о его внутреннем функционировании.

    В идеале они могут рассуждать об этом исключительно с точки зрения:

    Input
      ↓
    Options
      ↓
    User interaction
      ↓
    Selection
      ↓
    Callback
    

    вместо того чтобы:

    Dialog state
    Modal state
    List state
    Search state
    Checkbox state
    Rendering state
    ...
    

    Хорошо спроектированный API компонента скрывает эту внутреннюю сложность от пользователя.

    Это один из самых ясных уроков, полученных в процессе создания и совершенствования этого пакета.

    Открытый исходный код меняет подход к разработке

    Написание компонента для собственного приложения — это другая задача, чем написание его для более широкой аудитории разработчиков.

    Внутри одного приложения требования могут быть следующими:

    «Это решает мою конкретную проблему.»

    Как только проект становится открытым исходным кодом, вопрос меняется на:

    «Будет ли этот API понятен разработчику, который никогда не видел исходного кода?»

    Такое изменение влияет на приоритеты в процессе разработки.

    Вдруг вы начинаете гораздо внимательнее следить за такими аспектами:

    • Согласованность в API
    • Документация
    • Обратная совместимость
    • Крайние случаи
    • Разумные значения по умолчанию
    • Определения типов
    • Варианты настройки
    • Производительность
    • Версионирование

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

    Как проект развивался со временем

    То, что начиналось как простой компонент для выбора, постепенно превратилось в более полный набор инструментов для выбора.

    Его развитие можно приблизительно описать следующим образом:

    Simple Select
          ↓
    Single Selection
          ↓
    Multi Selection
          ↓
        Search
          ↓
    Large List Handling
          ↓
        Chips
          ↓
       Sections
          ↓
    Radio Selection
          ↓
    Bottom Sheet
          ↓
    Menu Presentation
    

    Каждая новая функциональность решала реальную проблему, связанную с удобством использования или опытом разработчика.

    Это один из аспектов разработки в открытом доступе, который выделяется среди прочих.

    Когда вы выпускаете версию 1, у вас редко бывает чёткое представление о том, как в итоге будет выглядеть готовая библиотека.

    Вместо этого структура пакета постоянно меняется по мере появления реальных требований, отзывов пользователей и новых сценариев использования.

    1. Незначительные проблемы могут превратиться в ценные проекты с открытым исходным кодом

    Изначальная проблема никак не была прорывной.

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

    Но существует значительная разница между устранением проблемы для собственных нужд и доработкой её до такого уровня, чтобы другие люди тоже могли её использовать.

    Иногда эта разница бывает достаточно велика, чтобы оправдать начало проекта с открытым исходным кодом.

    2. Чтобы что-то стало повторно используемым, его необходимо правильно абстрагировать

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

    Вместо этого он должен предоставлять чистый, простой интерфейс снаружи, скрывая при этом свою внутреннюю сложность.

    Этот принцип применим далеко не только в мире React Native.

    3. Проектирование компонентов также связано с пользовательским опытом

    Способность компонента быть повторно использованным на уровне кода — это не всё.

    Не менее важно то, каково ощущение от взаимодействия с ним.

    Диалоговые окна, нижние панели, меню, иконки, поля поиска, радио-кнопки и разделенные списки — всё это просто разные варианты ответа на один и тот же основной вопрос:

    Какой лучший способ позволить пользователю сделать выбор?

    4. Повторно используемые компоненты должны создаваться с учетом производительности

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

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

    5. Документация — это основная часть пакета, а не дополнение

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

    У разработчиков по-прежнему требуются четкие ответы на следующие вопросы:

    • Как его установить
    • Как им пользоваться
    • Какие свойства доступны
    • В каком формате должны быть данные
    • Как настроить его поведение и внешний вид
    • Какие функции действительно поддерживаются

    Качественная документация помогает преодолеть разрыв между ситуацией, когда кто-то думает:

    "I found this package."
    

    и моментом, когда он может уверенно сказать:

    "I can use this package."
    

    Как будет развиваться проект дальше

    Изначально цель проекта react-native-paper-select была скромной: создать компонент для выбора, который естественно впишется в приложения, уже использующие React Native Paper.

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

    Ещё много возможностей для его дальнейшего улучшения.

    Ни один проект с открытым исходным кодом никогда не становится полностью завершённым.

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

    Именно эта постоянная эволюция делает поддержку такой библиотеки по-настоящему увлекательной.

    Работа над react-native-paper-select показала, что проект с открытым исходным кодом не обязательно должен начинаться с великой идеи.

    Часто он возникает из-за небольшой, конкретной проблемы, с которой сталкиваешься в процессе создания чего-то другого.

    Вы решаете её для себя.

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

    Поэтому вы совершенствуете его.

    Вы правильно описываете его.

    Вы публикуете его.

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

    Вот история создания react-native-paper-select.

    Если ваш проект использует React Native Paper и вам нужен гибкий компонент для выбора одного или нескольких элементов, репозиторий находится на GitHub, а сам пакет опубликован на npm.

    Мы всегда рады получать отзывы, сообщения об ошибках и ваши вклады, если вы решите использовать этот пакет.