DEV Community

Cover image for React Native: Promesas y Callbacks en módulos nativos
Matías Hernández Arellano
Matías Hernández Arellano

Posted on • Edited on • Originally published at matiashernandez.dev

React Native: Promesas y Callbacks en módulos nativos

Durante estas últimas semanas he estado trabajando con un proyecto utilizando React Native, y si bien React Native es, de alguna manera, 90% simplemente React, es tambien cierto que crea nuevos desafios.

Recomendado: Comenzando con React Native

Algunos de estos:

  • desafíos son el nuevo ambiente de desarrollo: El ciclo de desarrollo y feedback es un poco más lento o forzoso en comparación con el rápido feedback obtenido al desarrollar una web utilizando por ejemplo create-react-app.
  • Diferentes primitivas: Las primitivas utilizadas para crear la interfaz en una aplicación React Native son diferentes y si bien en mi caso esto no fue algo nuevo ya que he hecho algunas otras app con React Native, si implica tener que mirar la documentacion basstante seguido para revisar algunas props o algunos elementos disponibles.
  • Es necesario implementar modulos nativos para resolver alguna tareas: Si bien la API de React Native y de herramientas como expo cubren gran parte de los casos de uso de una aplicación móvil genérica, muchas veces tenemos que crear nuestros propios módulos para resolver casos más complejos.

Relacionado: Agregar módulos nativos a una aplicación React Native

Es en este último desafío es en donde pase la última semana, implementando un wrapper en Java para exponer una API de un SDK.

Cuando desarrollas una aplicación web utilizando Javascript el uso de funciones asíncronas es parte del día a día y para lograr esa asincronicidad hay dos métodos posibles: Callbacks y Promesas.

Callbacks

Estas son parte esencial de Javascript y han estado presentes desde el inicio de los tiempos. ¿Que son?, de forma muy simple, una callback es una función que es pasado como argumento a otra función y es después llamada desde el interior de la función cuando termina de ejecutar alguna tarea.

Un ejemplo es el uso de setTimeout

const customSetTimeout = (callback) => {
        setTimeout(() => { //Esta tambien es un callback
            console.log('Set timeout terminado')
            callback(); //Ejecuta el callback cuando setTimeout termino
        }, 1000); //1 segundo
    }
Enter fullscreen mode Exit fullscreen mode

Promises

Una Promesa es una implementación un poco más robusta para resolver el problema de asincronicidad. En palabras generales, una promesa es “algo que pasará en el futuro”, basicamente es una función que recibe una instrucción de hacer alguna tarea y te responde “Aún no tengo los dato, pero dame tu contacto y cuano tenga los dato de aviso”. ¿Cómo se ve en código?

    fetch('https://someapi.com').then(data => {
        doSomethingWithData(data)
    }).catch(e => {
        console.error(e)
    })
Enter fullscreen mode Exit fullscreen mode

Para “capturar” esta promesa se utiliza then para definir que hacer cuando la promesa retorna “se resuelve” y catch para definir que hacer cuando la promesa falla.

Actualmente Javascript ofrece algo de syntaxis sugar utilizando async/await

    const getData = async () => {
        try {
            const data = await fetch('https://someapi.com')
            doSomethingWithData(data)
        }catch(e){
            console.error(e)
        }
    }
Enter fullscreen mode Exit fullscreen mode

Módulos Nativos

React Native ofrece algunas estructuras de datos que permiten ofrecen esta experiencia en los métodos expuestos. Existen dos estructuras utilizadas como argumentos Callback y Promise.

Callbacks Nativas

Callback es utilizado para proveer el resultado del llamado de la función hacia Javascript.
En el caso de iOS utilizando Objective-C

    RTC_EXPORT_METHOD(nativeFetchApi:(RCTResponseSenderBlock)callback)
    {
      NSArray *array = AwesomeSDK.fetch();
      callback(@[NSNull null], array);
    }
Enter fullscreen mode Exit fullscreen mode

O en su version Java

    @ReactMethod
    public void nativeFetchApi(Callback callback) {
        WritableArray array = AwesomeSDK.fetch() 
        callback.invoke(array)
    }
Enter fullscreen mode Exit fullscreen mode

Y después simplemente se utiliza en nuestro código Javacript

    const fetchCallback = (data) => {
        doSomethingCool(data)
    }
    NativeModules.MyAwesomeModule.nativeFetchApi(fetchCallback);
Enter fullscreen mode Exit fullscreen mode



El módulo nativo debe invocar el callback sólo una vez. También es posible almacenar el callback y llamarlo en otro punto del código. De hecho esto es lo que tuve que realizar ya que el SDK utilizado tiene sus propios
observers`.

Java
`java
private Callback privateCallback = null;
@ReactMethod
public void nativeSdkSingin(Callback callback) {
this.privateCallback = callback;
AwesomeSDK.signin()
}

@Override
public void signinObserver() {
    if(this.privateCallback != null) {
        this.privateCallback.invoke();
    }38k
    this.privateCallback = null;
}
Enter fullscreen mode Exit fullscreen mode

`

Promises Nativas

React Native también provee de una estructura de datos que puede definir una Promesa, que permite simplificar un poco el código asíncrono, sobre todo si se utiliza async/await. Para definir el uso de una promesa, es decir, determinar que una función retornará una promesa debes utilizar como último parámetro de la función este argumento.

En el caso de iOS utilizando Objective-C
objectivec
RTC_EXPORT_METHOD(nativeFetchApi,
nativeFetchApiWithResolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSArray *array = AwesomeSDK.fetch();
if(array){
resolve(array)
}else{
NSError *error =....
reject(@"no_data", @"No hay datos", error)
}
}

O en su version Java
`java
@ReactMethod
public void nativeFetchApi(Promise promise) {
try {
WritableArray array = AwesomeSDK.fetch()
promise.resolve(array)
}catch(...){
promise.reject('...')
}

}
Enter fullscreen mode Exit fullscreen mode

`
Y despues simplemente se utiliza en nuestro código Javacript
`
javascript
const fetchNativeData = async () => {
try {
const data = await NativeModules.MyAwesomeModule.nativeFetchApi();
doSomethingCool(data)
}catch(e){
console.error(e)
}
}
`
Y de igual forma que en el ejemplo de uso de callbacks, también es posible almacenar la promesa y llamarla en otro punto del código.

Java
`java
private Promise privatePromise = null;
@ReactMethod
public void nativeSdkSingin(Promise promise) {
this.privatePromise = promise;
AwesomeSDK.signin()
}

@Override
public void signinObserver() {
    if(this.privatePromise != null) {
        this.privatePromise.resolve();
    }38k
    this.privatePromise = null;
}
Enter fullscreen mode Exit fullscreen mode

`

Conclusión

Al desarrollar aplicaciones móviles utilizando React Native, ciertos casos de usos requieren el desarrollo de módulos nativos, y uno de los objetivos de estos módulos es ofrecer una experiencia de uso (al desarrollador) lo más cercana posible a una librería escrita en javascript, para esto React Native ofrece estructuras de datos que permiten desarrollar métodos nativos que soportan Callbacks y Promesas.

Top comments (0)