Client guide to React Native modules

Isaías Chávez
June 2024
8 min read

Summary

Este artículo lo escribí porque muchas veces me piden ayuda para portar SDKs o librerías a React Native y siempre termino explicando los mismos conceptos. Aquí dejo una guía clara de cómo funciona el ecosistema: JSI, Turbo Modules, Expo Modules, Nitro y los módulos en C++. Explico para qué sirve cada opción, qué sacrificios implica y cuándo conviene usar una u otra. Mi intención es que cualquier cliente o equipo entienda el panorama sin tener que pelearse con documentación fragmentada o conceptos poco explicados.

A veces mis clientes me llegan con la misma duda: "Queremos portar nuestro SDK o módulo a React Native, pero todo lo de JSI, TurboModules y la nueva arquitectura es un desastre, ¿nos puedes ayudar?". Claro que puedo. Pero como estoy cansado de repetir lo mismo, aquí dejo una explicación completa.

Old arch

La arquitectura vieja ya no existe. Era básicamente un puente JSON que serializaba datos entre JavaScript y el mundo nativo. Simple, pero lento.

New arch (o simplemente, la arquitectura actual)

React Native tenía fama de ser lento, y mucho era culpa del intercambio JSON. Para solucionarlo se creó la nueva arquitectura, formada por tres pilares: JSI, Fabric y TurboModules.

JSI

El cuello de botella principal era el paso de datos entre JS y nativo. Así nació JSI: un conjunto de APIs en C++ que permite interactuar directamente con el motor JS sin convertir todo a JSON. Es similar al Node-API, pero más flexible y adaptado al entorno móvil. La parte compleja es que ahora interactúas directamente con la "máquina virtual" del JS engine (Hermes o JSC), y eso implica manejar tiempos, callbacks y memoria con cuidado.

JSI es la base de todo lo demás.

Fabric

Fabric es el sistema que usa RN para renderizar UI usando JSI. Casi no afecta la creación de módulos; solo importa al registrar componentes nativos.

Turbo Modules

Los Turbo Modules son módulos nativos habilitados para JSI. A partir de un archivo TypeScript o Flow generan automáticamente gran parte del código puente en C++. Son rápidos y permiten inicialización perezosa, pero el sistema de codegen es frágil, cambia mucho entre versiones y la documentación deja bastante que desear.

Expo Modules

Expo detectó que la curva de aprendizaje para crear módulos con TurboModules era ridícula, así que creó su propia solución. Expo Modules reduce drásticamente el boilerplate y evita el codegen. Son fáciles de implementar, pero no tan rápidos como TurboModules o JSI puro. Para la mayoría de apps son suficientes.

JSI C++ Modules

Si tu módulo es puramente computacional (SQLite, Rust, C libraries), puedes escribir un módulo 100% en C++ sin tocar Swift o Kotlin. Son los más rápidos, pero también los más difíciles de configurar y tienen cero documentación actual. Una vez que funcionan, son casi inquebrantables.

Nitro Modules

Nitro Modules (creados por Marc Rousavy) abstraen patrones del JSI puro en C++ para simplificarlo. Son extremadamente rápidos y más sencillos que JSI raw, pero siguen siendo una solución de terceros.

Node API Modules

Este enfoque experimental permite crear módulos de RN usando NAPI. En teoría puedes usar módulos de Node.js en RN. Requiere Hermes moderno y aún no está claro si será adoptado ampliamente.

Which should you pick?

Depende del caso:

  • Ya usas Expo y no necesitas máximo rendimiento → Expo Modules
  • Máximo rendimiento y dominio de C++/ObjC/Java → JSI C++ Modules
  • Quieres velocidad sin tanto dolor nativo → Nitro Modules
  • Buen rendimiento sin depender de terceros → Turbo Modules
  • Apuestas por compatibilidad con Node → NAPI Modules

QA

¿Se puede crear un Turbo Module compatible con old arch?

No. Old arch desapareció.

¿Te gustan los Turbo Modules?

No mucho. Son frágiles y el codegen cambia demasiado.

¿Y Expo?

Si te funcionan, adelante. Expo ya resolvió su propio ecosistema.

¿Por qué si JSI es C++ veo Swift/ObjC/Kotlin/Java en TurboModules?

Porque se salta entre lenguajes: Swift → ObjC++ → C++. Kotlin/Java → JNI → C++. Y JNI es lento.

¿Puedo escribir TurboModules en Swift?

No por ahora. Swift 5.9 mejoró compatibilidad, pero no lo suficiente.

¿Cuándo será posible?

No hay fecha. Depende de Meta.

¿Puedo escribir módulos en Rust?

Sí, pero indirectamente: exportas funciones en C y luego las llamas desde C++/JSI.

¿Qué trampas comunes hay?

No puedes llamar funciones JSI cuando el VM está ocupado; puedes corromper memoria. Debes usar call invokers, esperar callbacks, etc. Es fácil encontrar dragones si no respetas el ciclo del runtime.