Lo scenario dei framework per lo sviluppo di applicazioni multipiattaforma è in continuo movimento. Dopo la grande passione per Ionic, sembra venuto il momento di React Native. Vediamo insieme vantaggi e limiti del paradigma semi-nativo.

Da un anno e questa parte si fa un gran parlare di React Native, framework per lo sviluppo di applicazioni su piattaforme native iOS, Android e Windows. In realtà il lancio ufficiale di questo importante progetto open source da parte di Facebook risale a tre anni fa. La comunicazione del primo rilascio di React Native è infatti del “lontano” 2015. Oggi su GitHub è disponibile la versione 0.55, rilasciata nel marzo 2018. Ma già si sta lavorando alla 0.56.

[Aggiornamento del 20 ottobre 2019: la versione più recente di React Native a oggi è la v0.61.2. Nel frattempo si registra il successo crescente di Flutter. Per questo abbiamo pubblicato un’analisi dedicata proprio al confronto tra React Native e Flutter]

Per chi sviluppa applicazioni multipiattaforma e multidispositivo, React Native sembra diventato la panacea di ogni problema. Al punto che sta sostituendo, nella preferenza di molti, il framework Ionic basato su Cordova e AngularJS. Anche Spindox, che da oltre un anno lavora sul confronto fra AngularJS e React, ha sposato il nuovo paradigma. Alcune delle nostre realizzazioni più recenti adottano proprio l’approccio cosiddetto semi-nativo. Sì, perché la prima cosa da ricordare è che, mentre Ionic è un framework per lo sviluppo di applicazioni ibride, React Native si avvicina al “vecchio” concetto di applicazione nativa. Diciamo che sta in mezzo fra il mondo ibrido e quello nativo.

native web

In comune c’è JavaScript

Il fatto di esserci aperti al nuovo paradigma non significa avere gettato alle ortiche tutta l’esperienza maturata con Cordova e AngularJS. Anche perché non sempre React Native è la giusta alternativa.

Vediamo dunque di fare un po’ di chiarezza, cominciando da ciò che le due filosofie hanno in comune. Alla base di entrambi i framework, infatti, c’è JavaScript. React Native si basa su ReactJS, la libreria JavaScript creata da Facebook nel 2011. Per questo motivo uno sviluppatore di React Native dovrebbe avere una buona conoscenza di JavaScript. Ma, com’è noto, anche AngularJS si basa su JavaScript. In questo caso, però, più che di libreria sarebbe corretto parlare di framework. AngularJS è infatti finalizzato all’organizzazione dell’architettura applicativa, più che allo sviluppo di singole funzionalità. La confusione tra framework e libreria è spesso causa di grossi equivoci.

Dunque, sia React Native sia AngularJS applicano i principi della programmazione dichiarativa, caratteristica del web. Lo stile dichiarativo offre grandi vantaggi. Esso permette di controllare il flusso e lo stato dell’applicazione, descrivendo come una certa entità debba apparire. Lo stile imperativo, viceversa, consente di controllare l’applicazione prescrivendo in che modo un’entità debba essere creata.

Da qui in poi cominciano le differenze.

WebView, non mi servi più

Ionic con AngularJS si avvale di WebView. Semplificando al massimo, potremmo dire che una WebView è un mini browser privo di comandi e configurato per l’esecuzione a schermo interno, che dialoga con il sistema operativo tramite API native. Al suo interno si compie il rendering di pagine web formate da JavaScript, HTML e CSS.

React Native, invece, non usa WebView. Per il rendering della app si serve di componenti atomici, detti appunto React Components. Tali elementi consentono di dividere l’interfaccia utente in parti indipendenti e riutilizzabili. Ciascuno di essi mappa infatti il corrispondente componente nativo di iOS o Android. Si tratta, com’è facile intuire, di un approccio che permette un controllo molto più puntuale del software, sia in fase di unit test sia quando si vogliano creare componenti complessi includendone altri più elementari. Senza contare che numerose librerie di React Components sono oggi disponibili per gli sviluppatori alle prime armi o che hanno fretta. È il caso di React Material-UI, React-Bootstrap, React toolbox, React Belle, Material Components Web, Ant Design React, Semantic UI React, Onsen UI e React Virtualized.

Dal DOM al Virtual DOM: l’astrazione di un’astrazione

Un altro punto di forza di React Native è costituito dal Virtual DOM (VDOM). Esso crea una struttura dei dati in-memory, lavora per differenza e quindi aggiorna in modo efficiente la DOM visualizzata dal browser.Ciò consente di scrivere codice come se l’intera pagina fosse renderizzata ad ogni modifica, mentre in realtà le librerie React eseguono il rendering solo dei sottocomponenti che sono effettivamente modificati.

Com’è noto, il DOM (Document Object Model) è una forma di rappresentazione dei documenti strutturati, che risponde all’obiettivo di renderli neutrali rispetto alla lingua e alla piattaforma. Nel mondo web, in particolare, il DOM è un’astrazione del codice HTML. O – detto altrimenti – una sua rappresentazione in-memory. Grazie al DOM è possibile accedere e aggiornare dinamicamente il contenuto, la struttura e lo stile dei documenti.

Il Virtual DOM è dunque l’astrazione del DOM, ossia l’astrazione di un’astrazione. Si tratta di un concetto di programmazione in cui una rappresentazione ideale, o “virtuale”, di un’interfaccia utente viene mantenuta in memoria e sincronizzata con il DOM “reale”, facendo uso di un’ulteriore libreria come ReactDOM. Questo processo si chiama riconciliazione.

I vantaggi sono evidenti. Il Virtual DOM di React, ossia la capacità di descrivere in modo dichiarativo un’interfaccia utente e modellarne lo stato, abbassa infatti le basse barriere in ingresso per uno sviluppatore JavaScript.

Come funziona il flusso di aggiornamento

L’interfaccia di un componente di React Native è espressa chiamando il suo metodo render(). Questo può accedere allo stato corrente e alle proprietà, secondo il seguente flusso:

1. Si esegue il metodo render() il quale riceve i dati in input e restituisce il Virtual-DOM, ossia l’albero dei contenuti da visualizzare.

2. A ogni modifica dei dati il metodo render() è eseguito e genera un nuovo Virtual-DOM.

3. Per ciascun componente, vengono calcolate le differenze tra il Virtual-DOM corrente e quello relativo al suo stato precedente, in modo tale da avere il DOM reale sempre aggiornato.

React Native vince in facilità e performance

Uno dei punti deboli di AngularJS è sicuramente la sua complessità. Di contro, con React Native lo sviluppo di nuovi componenti è relativamente semplice, grazie alla struttura atomica precedentemente descritta. Inoltre, l’intero ciclo di sviluppo risulta molto più rapido. Di fatto React Native è un’applicazione nativa, che interpreta la business logic scritta in JavaScript. Quindi non è necessario ricompilare l’intera applicazione ad ogni modifica del codice. Ma anche il Virtual DOM aiuta a risparmiare tempo, in quanto permette di calcolare qual è il minor numero di operazioni da effettuare per modificare il DOM.

Dal punto di vista delle performance, poi, React Native ha almeno un pregio rispetto ad AngularJS: si avvantaggia del fatto di non delegare alla WebView il rendering dell’interfaccia utente.

E i punti deboli?

Ovviamente anche React Native ha i suoi punti deboli. Qui ci limitiamo a segnalare quelli più significativi, rimandando al bel post di Hardik Shah per un quadro più completo.

Tanto per cominciare non è sempre agevole creare un’esperienza utente omogenea fra tutti i tipi di piattaforma. Succede cioè che, in alcuni casi d’uso, un componente si comporti in modo differente su iOS e su Android. Ciò dipende dal fatto che l’offerta di componenti di navigazione è ancora limitata. Peraltro, la situazione sta migliorando di giorno in giorno, come dimostra il numero di commit su GitHub.

Il fatto poi che React Native crei un livello di astrazione al di sopra dell’applicazione si porta dietro un inconveniente. Infatti, ogni bug incontrato nel livello di astrazione può portare a bug inaspettati all’interno dell’applicazione stessa. Tali bug sono estremamente difficili da diagnosticare e da individuare. Avere un livello di astrazione in atto implica inoltre che si è dipendenti da servizi o librerie di terze parti per mantenere aggiornato il framework e impedirne la rottura.

Infine, va detto che per implementare alcune funzionalità e moduli nativi è necessaria una conoscenza dettagliata della piattaforma nativa corrispondente. React Native permette il refactoring di molti moduli personalizzati per tutti i sistemi operativi. Tuttavia per sfruttare i sensori del dispositivo, la fotocamera o le notifiche push è necessario l’intervento di sviluppatori iOS e Android.