Inicio / Artículos

Crear un componente select configurable para React Native Paper

Un recorrido por el diseño y la apertura del código de react-native-paper-select, que abarca búsquedas, chips de selección múltiple, listas seccionadas y los compromisos en cuanto al rendimiento.

Editor: REACTAPP.TOP12 min de lectura2663 palabras

Fuentes: I Built a React Native Select Component — and Turned It Into an Open-Source Package: `react-native-paper-select`

Crear un menú desplegable real en React Native casi nunca es tan sencillo como parece

Si has dedicado tiempo a intentar implementar un menú desplegable adecuado en React Native, ya sabes que “solo un menú desplegable” se convierte rápidamente en una tarea mucho más compleja.

Acabas necesitando gestionar el estado de selección, integrar la función de búsqueda, soportar múltiples selecciones, manejar listas largas, presentar la interfaz de forma que parezca nativa en dispositivos móviles, agregar validaciones y asegurarte de que todo encaje visualmente con el resto de tu aplicación.

Si tu proyecto ya está basado en React Native Paper, utilizar una biblioteca de interfaz separada solo para obtener un campo de selección funcional parece excesivo.

Esa falta de compatibilidad es exactamente el problema que fue creado este componente para resolver.

El objetivo era crear un componente select reutilizable que encajara perfectamente dentro de React Native Paper, funcionara tanto para selección simple como múltiple, y evitara que cada proyecto tuviera que volver a implementar una y otra vez el mismo patrón de interacción.

Así que se desarrolló desde cero.

Lo que comenzó como un componente de interfaz bastante específico acabó convirtiéndose en react-native-paper-select: un paquete de código abierto para React Native que ahora incluye función de búsqueda, selección múltiple, visualización basada en chips, listas segmentadas, selección con botones de radio, paneles inferiores, menús y una renderización de listas optimizada en términos de rendimiento.

Este artículo aborda la motivación detrás de su creación, cómo cambió el componente con el tiempo, las decisiones técnicas que lo moldearon y las lecciones aprendidas al transformar algo originalmente creado para una sola aplicación en una biblioteca de código abierto de uso general.

El problema

A primera vista, los menús desplegables parecen algo trivial.

Pero una vez que se comienza a implementar uno, surgen numerosas preguntas de diseño:

  • ¿Cómo se muestra el valor seleccionado actualmente?
  • ¿El campo necesita soportar una sola selección o varias?
  • ¿Cuál es la presentación adecuada para la lista de opciones?
  • ¿Cómo debería funcionar la búsqueda dentro de esa lista?
  • ¿Debería ser diferente el comportamiento en Android e iOS?
  • ¿Cómo se representan listas muy largas sin afectar el rendimiento?
  • ¿Cómo se mantiene simple la API para los desarrolladores?
  • ¿Cómo se permite la personalización sin convertir el componente en algo complicado de usar?

No tenía sentido resolver estas preguntas desde cero en cada aplicación individual.

En lugar de crear otro componente único específico para una aplicación, tenía más sentido diseñar algo reutilizable desde el principio.

La idea subyacente era sencilla:

Crear un componente select basado en las primitivas de interfaz de usuario propias de React Native Paper, y hacerlo lo suficientemente fácil como para que otros desarrolladores de React Native realmente quieran usarlo.

Presentamos react-native-paper-select

react-native-paper-select es un componente configurable para React Native que admite casos de uso tanto de selección única como múltiple.

La primera versión se basó en los componentes ya disponibles en React Native Paper, entre ellos:

  • TextInput
  • Checkbox
  • Dialog

El objetivo era ofrecer a los desarrolladores una experiencia de selección que fuera visual y funcionalmente consistente con las aplicaciones ya creadas utilizando el sistema de diseño de React Native Paper.

A medida que el paquete se fue perfeccionando, pasó de esa implementación inicial para soportar estilos de presentación adicionales y un comportamiento de selección más avanzado.

Instalación del paquete

Para ponerlo en marcha solo se necesitan un par de pasos.

npm install react-native-paper-select

O, si prefiere Yarn:

yarn add react-native-paper-select

Una vez instalado, importe el componente de la siguiente manera:

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

Un Select básico

Para crear un campo de selección mínimo, solo se necesita una lista de opciones y una función para manejar la selección.

Aquí tiene un ejemplo:

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

Y luego, renderizar el componente:

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

Lo importante aquí no es cuán poco código se necesita.

Sino lo que ese código oculta.

Desde el punto de vista de la aplicación, en realidad solo hay dos preguntas que responder:

¿Qué opciones están disponibles?

y

¿Qué eligió el usuario?

Todo lo relacionado con la presentación e interacción es gestionado internamente por el propio componente.

Selección única vs Selección múltiple

Soportar tanto la selección única como la selección múltiple desde el principio fue uno de los requisitos fundamentales para este componente.

La selección única es bastante sencilla de entender:

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

No obstante, la selección múltiple implica gestionar un estado adicional:

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

Eso significa que el componente debe encargarse de:

  1. Seguir el estado de selección
  2. Gestionar la deselección
  3. Mostrar varios valores seleccionados al mismo tiempo
  4. Devolver el conjunto actualizado de selecciones
  • Mantener la interfaz de usuario mostrada sincronizada con los datos subyacentes
  • La intención era contener toda esa complejidad dentro del propio componente, en lugar de transferirla a cada desarrollador que utilizara el paquete.

    ¿Por qué React Native Paper?

    Dado que la aplicación existente ya dependía de React Native Paper, construir el componente select sobre ese mismo sistema de diseño fue una elección natural.

    En lugar de reinventar cada elemento visual, el componente podía reutilizar las piezas de interfaz que Paper ya proporcionaba.

    A un nivel alto, la estructura interna se veía más o menos así:

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

    Esa elección de diseño trae una ventaja real.

    Cualquier aplicación que ya utilice React Native Paper puede incorporar este componente select sin introducir un estilo visual que choque con el resto de la interfaz.

    De diálogo a múltiples estilos de presentación

    La primera versión del componente solo ofrecía un diálogo para realizar una selección.

    La interacción funcionaba de la siguiente manera:

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

    No obstante, a medida que el componente se fue desarrollando, resultó evidente que un diálogo no siempre era el patrón adecuado para todas las pantallas móviles.

    Las necesidades de experiencia de usuario varían mucho de una aplicación a otra.

    Para tener en cuenta esto, el componente pasó a soportar varios modos de presentación:

    • Diálogo
    • Hoja inferior
    • Menú

    Con este cambio, el mismo componente de selección subyacente puede utilizarse en contextos de interacción muy diferentes, y los desarrolladores no tienen que reescribir su integración para cambiar entre ellos.

    Por ejemplo:

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

    Esta flexibilidad supuso un avance significativo para la biblioteca.

    La lógica de selección permanece idéntica sin importar qué estilo se elija; solo cambia la forma en que se presenta al usuario.

    Manejo de listas largas

    Un desafío menos obvio con los componentes de tipo desplegable es el rendimiento de la lista a medida que crece.

    Imagínese un componente select con:

    10 items
    

    Eso es sencillo de renderizar.

    Ahora imagínese uno con:

    1,000 items
    

    o incluso:

    10,000 items
    

    Renderizar tantos elementos de lista a la vez puede comenzar a afectar la capacidad de respuesta de una aplicación móvil.

    Para evitarlo, el paquete depende de FlashList para renderizar sus listas de opciones.

    El concepto es sencillo:

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

    El rendimiento no es un problema que pueda posponerse hasta que se esté creando algo a gran escala.

    Cualquier componente destinado a ser reutilizado debe construirse teniendo en cuenta la escalabilidad desde el principio, ya que rara vez se sabe de antemano cuán grandes serán los conjuntos de datos que se le pasarán.

    Búsqueda

    A medida que aumenta la cantidad de opciones seleccionables, la búsqueda se vuelve esencial.

    En lugar de hacer que los usuarios naveguen interminablemente por una lista larga, el componente ofrece una forma de buscar para reducir las opciones.

    El flujo es el siguiente:

    Search
      ↓
    Filter options
      ↓
    Select result
      ↓
    Update selection
    

    Puede parecer una adición menor desde el punto de vista del usuario, pero tiene un gran impacto en la usabilidad una vez que el conjunto de datos supera unas pocas entradas.

    Selección múltiple con chips

    Mostrar varios valores seleccionados al mismo tiempo puede volverse rápidamente caótico.

    Por ejemplo, un valor como:

    React Native, TypeScript, Node.js, MongoDB
    

    es difícil de interpretar cuando se coloca en un campo de texto estándar.

    Los chips resuelven ese problema.

    Los valores seleccionados pueden mostrarse en su lugar como bloques separados y distintos:

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

    El componente admite esta pantalla basada en chips para escenarios de selección múltiple, lo que brinda a los usuarios una visión mucho más clara de lo que han elegido.

    También hace que eliminar selecciones individuales sea mucho más intuitivo.

    Listas seccionales

    Otra exigencia que surgió fue la posibilidad de agrupar opciones relacionadas entre sí.

    En lugar de una lista larga y plana como:

    React
    Angular
    Vue
    Node.js
    Java
    Python
    MongoDB
    PostgreSQL
    

    las opciones pueden organizarse bajo encabezados:

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

    Agrupar de esta manera hace que los grandes conjuntos de opciones sean mucho más fáciles de escanear y comprender.

    En el modo de selección múltiple, permitir que los usuarios seleccionen toda una sección a la vez acelera considerablemente el proceso.

    Por ejemplo:

    ☑ Frontend
    
        ☑ React
        ☑ Angular
        ☑ Vue
    

    Esto es mejor que obligar a alguien a tocar cada elemento uno por uno.

    Botones de radio para selección única

    Las casillas de verificación son adecuadas cuando se puede elegir más de una opción.

    Pero en escenarios de selección única, los botones de radio transmiten la intención de manera más clara al usuario.

    Por eso el paquete también ofrece una variante con botones de radio para la interfaz de selección.

    La idea fundamental permanece constante:

    Options
       ↓
    User chooses one
       ↓
    Selection state updated
    

    Solo cambia la presentación visual.

    Esto destaca un principio importante para crear componentes reutilizables:

    Mantenga la lógica de selección separada de cómo se muestra dicha selección.

    TypeScript y diseño de API

    Dado que está destinado a ser utilizado por otros desarrolladores, la estructura de la API es tan importante como el aspecto de la interfaz.

    Quien utilice el paquete no debería necesitar saber nada sobre su funcionamiento interno.

    Lo ideal es que puedan razonar al respecto exclusivamente en términos de:

    Input
      ↓
    Options
      ↓
    User interaction
      ↓
    Selection
      ↓
    Callback
    

    en lugar de:

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

    Una API de componente bien diseñada oculta esa complejidad interna al usuario final.

    Esa es una de las lecciones más claras que surgieron al crear e iterar sobre este paquete.

    El código abierto cambia la forma en que se construyen las aplicaciones

    Escribir un componente para su propia aplicación es una tarea diferente a escribir uno dirigido a un público más amplio de desarrolladores.

    Dentro de una sola aplicación, el estándar puede ser:

    "Esto resuelve mi caso específico."

    Una vez que se vuelve código abierto, la pregunta cambia a:

    "¿Tendría sentido esta API para un desarrollador que nunca ha visto el código fuente?"

    Ese cambio modifica sus prioridades a medida que desarrolla.

    De repente prestas mucha más atención a aspectos como:

    • Coherencia en toda la API
    • Documentación
    • Compatibilidad con versiones anteriores
    • Casos límite
    • Valores predeterminados razonables
    • Definiciones de tipos
    • Opciones de personalización
    • Rendimiento
    • Versionado

    Lo que comienza como un componente pequeño y simple puede convertirse en algo sorprendentemente complejo una vez que otros desarrolladores comienzan a depender de él en entornos de producción.

    Cómo progresó el proyecto con el tiempo

    Lo que empezó como un componente de selección sencillo se transformó gradualmente en una herramienta más completa para realizar selecciones.

    Puedes resumir su evolución de la siguiente manera:

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

    Cada nueva funcionalidad abordaba un problema real relacionado con la usabilidad o la experiencia del desarrollador.

    Ese es uno de los aspectos destacados de desarrollar en entornos abiertos.

    Cuando se envía la versión 1, rara vez se tiene una idea clara de cómo será finalmente la biblioteca terminada.

    En cambio, el paquete sigue cambiando a medida que surgen requisitos del mundo real, comentarios de los usuarios y nuevos casos de uso.

    Lecciones del proceso

    1. Los problemas menores pueden convertirse en proyectos de código abierto valiosos

    El problema inicial no era revolucionario en absoluto.

    Simplemente faltaba un componente select reutilizable.

    Pero existe una diferencia real entre arreglar algo para uso propio y perfeccionarlo hasta el punto en que otras personas también puedan utilizarlo.

    A veces esa diferencia es lo suficientemente grande como para justificar la creación de un proyecto de código abierto.

    2. Hacer que algo sea reutilizable implica abstraerlo adecuadamente

    Un componente bien diseñado no debería estar vinculado de forma estricta a la aplicación específica que lo originó.

    En lugar de eso, debe presentar una interfaz limpia y sencilla en el exterior mientras oculta su complejidad interna.

    Ese principio es aplicable mucho más allá del mundo de React Native.

    3. El diseño de componentes también se refiere a la experiencia del usuario

    Hacer que un componente sea reutilizable a nivel de código no es todo.

    También es igualmente importante cómo se siente al interactuar con él.

    Los diálogos, las hojas inferiores, los menús, las fichas, los campos de búsqueda, los botones de radio y las listas seccionadas son simplemente diferentes respuestas a la misma pregunta fundamental:

    ¿Cuál es la mejor forma para que alguien haga una selección?

    4. Los componentes reutilizables deben crearse teniendo en cuenta el rendimiento

    Un componente que funciona bien con diez elementos podría utilizarse posteriormente en un lugar con diez mil.

    Esa es precisamente la razón por la cual las decisiones relacionadas con la renderización de listas y la virtualización deben tomarse en la fase de diseño, y no como una solución posterior una vez que comienzan a aparecer problemas de rendimiento en producción.

    5. La documentación es una parte esencial del paquete, no algo adicional

    No basta con que el código funcione para considerar que un paquete está terminado.

    Los desarrolladores aún necesitan respuestas claras sobre:

    • Cómo instalarlo
    • Cómo usarlo
    • Qué propiedades están disponibles
    • Qué formato deben tener los datos esperados
    • Cómo personalizar su comportamiento y apariencia
    • Qué funciones están realmente soportadas

    Una documentación sólida es lo que cierra la brecha entre alguien que piensa:

    "I found this package."
    

    y llegar al punto en el que puede decir con confianza:

    "I can use this package."
    

    Adónde va a seguir el proyecto

    El objetivo inicial de react-native-paper-select era modesto: un componente de selección que se integrara de forma natural en aplicaciones que ya utilizaban React Native Paper.

    Desde entonces, se ha convertido en una herramienta mucho más versátil, que ahora incluye múltiples modos de selección, varios estilos de presentación, búsqueda, selección mediante chips, datos organizados en secciones y una renderización de listas optimizada para el rendimiento.

    Aún queda mucho espacio para seguir mejorándolo.

    Ningún proyecto de código abierto está realmente terminado.

    Siguen apareciendo nuevos dispositivos, React Native sigue lanzando actualizaciones, las expectativas de diseño cambian, las necesidades de accesibilidad evolucionan, vuelven a surgir consideraciones de rendimiento y los desarrolladores siguen enviando comentarios valiosos en los que actuar.

    Esa evolución continua es parte de lo que hace que mantener una biblioteca como esta sea realmente interesante.

    Pensamientos finales

    Trabajar en react-native-paper-select demostró que un proyecto de código abierto no necesita comenzar con una visión grandiosa.

    A menudo surge a partir de un problema pequeño y específico al que te enfrentas mientras desarrollas otra cosa.

    Tú mismo lo resuelves.

    Luego se hace evidente que esa solución podría ayudar a otros que tengan el mismo problema.

    Así que la perfeccionas.

    La documentas adecuadamente.

    La pones a disposición del público.

    Y con el tiempo, un componente interno sencillo puede convertirse en un paquete del que otros desarrolladores dependen para sus propios proyectos.

    Esa es la historia detrás de react-native-paper-select.

    Si su proyecto utiliza React Native Paper y necesita un componente de selección flexible o de selección múltiple, el repositorio se encuentra en GitHub, y el paquete en sí está publicado en npm.

    Si decide utilizar el paquete, siempre serán bienvenidos sus comentarios, informes de errores y contribuciones.