Saltar al contenido

Cómo creamos nuestra aplicación React Native

El año pasado lanzamos nuestra PWA con el objetivo de mejorar la experiencia de nuestros usuarios en conexiones de red lentas e inconsistentes. Fue el primer paso hacia la calidad de los productos por los que nos esforzamos. Recibimos una respuesta muy positiva de la comunidad y de nuestros clientes y queríamos replicar el mismo éxito también para nuestra aplicación móvil.

Una aplicación móvil ideal debería ser una extensión de la web móvil en lugar de ser un reemplazo.

Desafíos

  • Estamos construyendo experiencias en 3 plataformas diferentes, a saber: Android, iOS y la web (escritorio y móvil).
  • Esto significa la duplicación de la lógica empresarial en 4 bases de código, lo que no es lo mejor que puede hacer si utiliza DRY.
  • También significa que la introducción de nuevas funciones o la modificación de las existentes requiere realizar los cambios necesarios en 4 bases de código independientes. Esto no es escalable en absoluto y las plataformas pronto terminarían desincronizadas.
  • Finalmente, tendríamos que construir y expandir estratégicamente 3 equipos separados de desarrolladores para cada una de las 3 plataformas.

Objetivos

Para superar estos desafíos, decidimos apostar por la nueva generación emergente de aplicaciones nativas multiplataforma creadas con una pila de frontend moderna en JavaScript. Comenzamos a implementar las aplicaciones con los siguientes objetivos principales:

  • Aunque las aplicaciones estarían escritas en JavaScript, no deberían comprometer la experiencia y la capacidad de respuesta que los usuarios asocian con las aplicaciones ‘nativas’. En palabras más simples, si usted es el usuario, la aplicación debería sentirse como cualquier otra aplicación nativa en la App Store o Play Store.
  • La aplicación debería reutilizar la mayor cantidad de código posible en Android e iOS. Esto estaría en consonancia con el principio de SECO. También implicaría que mantener el código es mucho más fácil y agregar / modificar / eliminar funciones significa tocar la cantidad mínima de archivos posible.
  • Por último, pero no menos importante, nuestro equipo de ingenieros de productos para la web debería estar familiarizado con la pila utilizada y debería reducirse la dependencia de los desarrolladores nativos específicos de la plataforma. Esto también está en línea con el aumento del factor de autobuses en Housing.

Apilar

  • react-navigation: aún en sus inicios, pero resuelve el tema de la navegación tan debatido de manera declarativa utilizando la API animada. También encaja bien en nuestro sistema de gestión de estado basado en redux, ya que es una solución puramente basada en JS. Sin embargo, también estamos investigando otras soluciones de navegación nativas e híbridas.
  • redux-observable: el ecosistema JS todavía está descubriendo la mejor solución para la administración de estado asíncrono, pero al final, es más un problema de ‘cada uno lo suyo’. Decidimos usar redux-observable porque nos ayuda a aislar bien los efectos secundarios y manejarlos con los operadores de poder expresivo RxJS. Este enfoque también nos permite probar nuestro código de manejo de efectos secundarios de manera aislada.
  • inmutable: enfrentamos errores desagradables y difíciles de encontrar en plataformas anteriores que surgieron de mutaciones causadas en nuestros reductores. Para mitigar este problema de una vez por todas, decidimos utilizar estructuras de datos inmutables en toda la aplicación. Esto fue posible gracias a una fábrica de reductores personalizados que convierte entre estructuras de datos JS inmutables y vanilla.
  • ramda: en la medida de lo posible, nos propusimos codificar en un paradigma funcional y declarativo a través de funciones puras que manejan la mayor parte de nuestra lógica empresarial. Ramda ha sido insustituible para nosotros en ese sentido.
  • redux-persist: a diferencia de las aplicaciones web, las aplicaciones nativas tienen una noción de modo fuera de línea y estado persistente. Esta biblioteca, junto con redux-persist-migrate, resolvió elegantemente este problema con una capa AsyncStorage de respaldo.

Estampación

Además de los sospechosos habituales – hilo , más bonita, eslint y ronca, también dependemos de las siguientes herramientas:

  • libro de cuentos: proporciona un excelente soporte para el desarrollo de componentes nativos aislados. Como resultado, pudimos codificar nuestros componentes de interfaz de usuario como un mapeo uno a uno de nuestra guía de diseño. Estamos buscando implementarlo internamente para que los diseñadores también tengan acceso a componentes reales.
  • codepush: esta es un área donde las aplicaciones nativas de Reacción realmente brillan. Usamos codepush para publicar actualizaciones discretas por aire para nuestros usuarios mientras somos dueños de los porcentajes de implementación y las versiones de destino.
  • Fastlane: administrar diferentes entornos (preparación, desarrollo, producción) y automatizar nuestras compilaciones resultó ser muy sencillo con Fastlane. Expusimos un panel de compilación parametrizado en nuestro Jenkins CI interno que administra todo, desde secretos de aplicaciones, firma de código, pruebas de vuelo y cargas de Crashlytics Beta, registrando dispositivos para compilaciones de prueba internas, lanzando actualizaciones OTA a través de codepush, etc.

  • broma y desintoxicación: esta combinación resultó en una plataforma de prueba deliciosa para nuestra aplicación. Jest resultó ser un poco engorroso de configurar para react native dado el hecho de que tuvimos que escribir simulacros para módulos nativos, pero valió la pena el esfuerzo. La desintoxicación de la gente de Wix Engineering nos simplificó la historia de las pruebas de principio a fin.

  • sentry: la gente de sentry.io introdujo soporte de primera clase para aplicaciones nativas de reacción en algún momento. El nuevo SDK enriquece los informes de errores con una gran cantidad de datos útiles específicos del dispositivo y proporciona informes holísticos con seguimientos de pila tanto nativos como JS.

Más del 90% del código fuente de la aplicación está en JavaScript sin comprometer el rendimiento y la calidad.

Aprendizajes

React Native es un relativamente plataforma joven. La comunidad que lo rodea todavía está deliberando sobre las mejores prácticas y la manera correcta hacer ciertas cosas. Sin embargo, como punto de partida, los documentos oficiales son el mejor recurso con el que nos hemos encontrado. Aquí hay algunas cosas que aprendimos a lo largo del camino:

  • InteractionManager: este es su mejor amigo cuando se trata de perf. Ha habido un esfuerzo considerable por parte de la comunidad para mover cosas costosas para que se ejecuten en subprocesos nativos, ya que JS es de un solo subproceso. Hay momentos en los que necesita hacer cosas caras en JS sin afectar el rendimiento de sus animaciones / transiciones / interacciones de usuario. InteractionManager proporciona una buena API de programación para posponer estas costosas cosas hasta después de que se hayan completado dichas animaciones / transiciones / interacciones.
  • requestAnimationFrame: este se tomó prestado de la web y funciona de manera idéntica. Un caso de uso particular es el efecto dominó en los dispositivos Android. El enfoque habitual de usar aTouchableNativeFeedback con un controlador apt onPress no siempre funciona aquí. A veces, es posible que no vea la onda. En cambio, si envuelve su controlador onPress en un bloque requestAnimationFrame, notará que las animaciones son perfectamente visibles.
  • MessageQueue: React Native funciona comunicándose entre JS y los reinos nativos a través de un puente. Como resultado, hay una constante charla sobre este puente que puede afectar el rendimiento de manera adversa si no se modera adecuadamente. El método de espionaje en MessageQueue, como sugiere su nombre, le permite espiar esta charla y ver qué se envía. Esto podría ayudarlo a comprender lo que realmente está sucediendo debajo y mejorar el rendimiento.


MessageQueue.spy(true)

  • setNativeProps – De los documentos oficiales – “setNativeProps es el equivalente de React Native a establecer propiedades directamente en un nodo DOM”. A veces, por razones que solo usted conoce, es posible que desee manipular la vista nativa subyacente que respalda su vista JS mientras cortocircuita el ciclo de renderizado de reacción. Usamos esto solo en un par de lugares porque todo lo demás simplemente no funcionó lo suficientemente bien. Evite usarlo o utilícelo con mucha prudencia si es necesario.
  • Estructuración: desde el principio, seguimos una estructura organizativa simple para nuestro repositorio. Separamos nuestros tontos componentes de la interfaz de usuario de las vistas con estado. La gestión del estado se encargó de nuestras epopeyas y reductores. Observamos que el código de generación de efectos secundarios disperso aleatoriamente se convierte en el cuello de botella para mantener nuestra base de código funcional y comprobable. Nuestro enfoque con redux-observable nos ayudó a mitigar algunos de esos dolores. Considere el siguiente ejemplo:

Pudimos contener la mayor parte del código de efectos secundarios en una sola función en lugar de aprovechar los métodos del ciclo de vida de los componentes. Además, inyectamos el efecto secundario que genera dependencia: ajax en este caso, en la propia función. Esto puede ser reemplazado por algo que simplemente se burle de las solicitudes de red en un entorno de prueba.

  • Redux Middleware: dado que todo el estado de la aplicación vive en redux, incluida la navegación, el middleware redux se vuelve indispensable para ejecutar código en respuesta a acciones. En nuestro caso, delegamos la analítica (seguimiento de pantalla), el registro, el informe de errores, la modificación de la barra de estado del dispositivo y la gestión de la memoria a middleware dedicado. Esto elimina efectivamente este código de las vistas individuales y las mantiene optimizadas. Aquí hay un ejemplo que cambia entre una barra de estado oscura o clara en iOS según la pantalla actual:

Construir canalización

Los documentos oficiales proporcionan una gran cantidad de información sobre la API y la plataforma en sí. Sin embargo, al final, debe implementar su nueva aplicación brillante. Esto también implica desafíos como mantener múltiples entornos para las pruebas y la puesta en escena, incorporar diferentes credenciales de manera discreta, generar notas de la versión y notificar a todas las partes interesadas (gerentes de producto, probadores y diseñadores). Después de experimentar y luchar con varios enfoques, nos mudamos a Fastlane para automatizar todo este proceso. A continuación se muestra una versión abreviada de nuestro ciclo de lanzamiento beta en iOS:
Este fragmento de código maneja la firma de código, registra dispositivos para pruebas, incrementa los números de compilación, crea la aplicación, la carga en Crashlytics Beta, genera notas de la versión, la libera al enviar código y carga los mapas de origen a centinela, notificando en un slack channel y finalmente agregando una etiqueta de lanzamiento en GitHub. Potencialmente, puede hacer cualquier cosa relacionada con la construcción aquí. Este código se encuentra al lado del código de la aplicación principal. Dado que el CI extrae una versión nueva de nuestro repositorio antes de cada compilación, es ridículamente fácil modificar la canalización de compilación sin romper el CI.

Consejos profesionales

  1. Lea los documentos y las notas de la versión.
  2. yarn start —- –reset-cache – para cuando instaló algo y no funciona / no se puede encontrar.
  3. react-native-debugger: la aplicación independiente basada en el depurador oficial de React Native e incluye React Inspector / Redux DevTools.
  4. Haga del Monitor Perf incluido su mejor amigo.
  5. Pruebe siempre en un dispositivo real.
  6. Conocer React es un requisito previo.

Nota

Si esta publicación te entusiasmó con el tipo de trabajo que estamos haciendo aquí, estamos contratando. Encuéntrenos en Twitter @HousingEngg.

Sid, Bhavir, Ritesh, Vikas, Rahul, Amandeep y Dron trabajaron en el equipo de React Native Apps. Rohit y Harish se encargaron del control de calidad.

Descargo de responsabilidad: no abogamos por ninguna de las herramientas, bibliotecas, prácticas de codificación o filosofías de desarrollo de software mencionadas aquí. Le invitamos a leer, aprender, aceptar, rechazar y criticar como mejor le parezca.

Este contenido se publicó originalmente aquí.