Die Vorteile von React können nicht nur in Webanwendungen, sondern auch in nativen Anwendungen genutzt werden. React Native ist der Versuch von Facebook, React auch für Android, iOS (und in der Zukunft für Windows 10) zu nutzen.
Herausforderungen nativer App-Entwicklung
Im vergangenen Blog-Beitrag haben wir bereits eine React-Webapp gebaut. Nun werden wir auch eine native App bereitstellen, was wir natürlich für jede Plattform tun könnten.
Es ist schwierig, mobile Apps zu bauen. Wenn man eine Webapp baut, dann ändert man meist seine Quellen und lädt den Browser (i.d.R. automatisiert) neu. So sieht man sofort die Änderungen, die man im Code durchgeführt hat.
Native Entwicklung geht nicht so schnell von der Hand. Nach jeder Änderung wird die Anwendung neu kompiliert und auf das Endgerät (oder in den Emulator / Simulator) übertragen. Dann öffnet der Entwickler seine Anwendung erneut und navigiert zu der Stelle, an der er seine Änderungen vermutet. Dieser Entwicklungszyklus kann – vor allem bei großen Anwendungen – schmerzhaft langsam sein.
Auch der Release-Zyklus ist langsam: eine Website kann man beliebig oft deployen (per Continuous Delivery auch sehr oft, z.B. wöchentlich, täglich, stündlich oder noch häufiger). A/B-Tests, um eine Webanwendung zu optimieren, gehören zum Handwerkszeug eines Webentwicklers. Native Apps jedoch haben die Hürden eines App Stores. Updates müssen durch die Bergwerke Reviews von Google und Apple wandern. Das kann lange dauern. Die nutzerzentrierte Optimierung einer App ist so nur schwer möglich.
Um die DX (Developer Experience) der Webentwicklung auf native Entwicklung zu übertragen, hat man einen WebView in eine native App gepackt und im WebView entwickelt. Auf diesem Ansatz basiert z.B. Apache Cordova aka Adobe PhoneGap. Was wäre also leichter, unsere React-App einfach mit Cordova in eine native App einzupacken? Wir könnten weiter wie im Web entwickeln und hätten trotzdem eine „native“ App in den App Stores.
Leider leidet so unter der DX die UX (User Experience). Jede Plattform hat spezifische UI-Controls (wie Radio-Buttons, Date-Picker usw.). Natürlich kann man versuchen, diese UI-Controls mit Webtechnologien (HTML, CSS und JavaScript) nachzubauen, aber dies wird nie ganz gelingen (weder das Verhalten noch die Performance gleichen denen der nativen Controls). Zudem muss man so bei jedem Update einer Plattform seine eigenen Controls evtl. geringfügig anpassen (oder auch stark, wir erinnern uns an iOS 7!) Auch sind die Gestensteuerungsmöglichkeiten im Web beschränkter als in nativen Implementierungen. Daher fühlen sich „hybride“ Apps oft wie minderwertige Fremdkörper auf einem Smartphone an.
Was könnte man also tun, um die Developer Experience der Webanwendung auf native Apps zu übertragen, ohne dabei aber in einem WebView zu entwickeln? Man könnte einen JavaScript-Wrapper für native UI-Controls bauen und damit native Anwendungen in JavaScript programmieren. Hier gibt es allerdings ein Problem: JavaScript ist single threaded. Die komplette Anwendung würde im UI-Thread laufen – keine gute Idee.
Allerdings wissen wir bereits, dass React vom DOM abstrahiert. Es ist also nicht an einen Webbrowser gebunden. Zudem wissen wird, dass React-Komponenten rein („Purity“, „pure functions“) sind – sie sind seiteneffektfrei. Daher wäre es für Facebook möglich, React Native so zu implementieren, dass ein JavaScript-Thread sich um die Logik kümmert und ein UI-Thread auf Änderungen des Zustands von Komponenten reagiert und so die UI rendert. Genau das macht React Native. Es wird nicht – wie im Browser – HTML-gerendert. Statt dessen läuft React in einer eingebetteten Instanz von JavaScriptCore und es werden plattformspezifische, native UI-Widgets gerendert. Die Performance leidet nicht (besonders stark) darunter, da das Rendering vom eigentlichen Applikations-Thread getrennt ist. Es ist es asynchron (UI-Thread und Logik-Thread laufen parallel). Der JS-Thread arbeitet im Batch-Mode. Er läuft im Hintergrund, der Main Thread, der die UI aktualisiert, im Vordergrund.
Ein Projekt aufsetzen
Die Werkzeuge, mit denen man React Native entwickelt, sind solche, die in modernen Web-Projekten verwendet werden. React Native wurde für Webentwickler mit den Technologien des Webs geschaffen. Um ein Gefühl für React Native zu bekommen, kann man ein einfaches „Hello World“-Projekt entwickeln.
React Native hat ein Command Line Interface, das man über NPM installieren kann. Eine React-Anwendung wird dann mit dem CLI initialisiert.
[code]
npm install -g react-native-cli
react-native init AwesomeProject
[/code]
Diese App lässt sich live auf einem iOS-Simulator (oder einem Android-Gerät) entwickeln.
[code]
react-native run-android
react-native run-ios
[/code]
Im generierten Projekt findet man die index.ios.js
– bzw. die index.android.js
-Dateien. Diese können wir um etwas Beispiel-Code ergänzen, der illustriert, wie eine React Native-Anwendung aussehen kann.
[code language=“javascript“]
import React, {
AppRegistry,
Component,
StyleSheet,
Text,
View,
Image
} from ‚react-native‘;
const MOCKED_MOVIES_DATA = [
{
title: ‚Bad Taste‘,
year: ‚1987‘,
posters: {
thumbnail: ‚https://upload.wikimedia.org/wikipedia/en/9/9f/Bad_taste_poster.jpg‘
}
},
];
class AwesomeProject extends Component {
movie = MOCKED_MOVIES_DATA[0];
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
{this.movie.title}
</Text>
<Text>Year: {this.movie.year}</Text>
<Image source={{uri: this.movie.posters.thumbnail}} style={styles.thumbnail}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: ‚center‘,
alignItems: ‚center‘,
backgroundColor: ‚#F5FCFF‘,
},
welcome: {
fontSize: 20,
textAlign: ‚center‘,
margin: 10,
},
thumbnail: {
width: 130,
height: 200
},
});
AppRegistry.registerComponent(‚AwesomeProject‘, () => AwesomeProject);
[/code]
Das Ergebnis ist ein Screen, der ein Filmplakat darstellt.
Styling und Layout
Gestylt wird die App anscheinend per CSS. Wenn man sich den Code jedoch genauer ansieht, dann sieht man, dass keine Stylesheets, sondern JavaScript-Objekte verwendet werden.
[code language=“javascript“]
{
container: {
flex: 1,
justifyContent: ‚center‘,
alignItems: ‚center‘,
backgroundColor: ‚#F5FCFF‘,
},
welcome: {
fontSize: 20,
textAlign: ‚center‘,
margin: 10,
},
thumbnail: {
width: 130,
height: 200
},
}
[/code]
Dies hat den Vorteil, dass das Styling der Komponenten, die in JavaScript implementiert sind, ebenfalls per JavaScript gelöst wird. So ist eine echte Modularisierung in einer einzigen Sprache möglich. Dieser Ansatz ist gleich dem Ansatz, das Markup in React in JavaScript und nicht in einem ausgelagerten Template zu halten.
Alle Core-Komponenten haben ein Style-Attribut, dem man einzelne Styles oder auch ein Array von Styles zuweisen kann.
[code language=“html“]
<Image source={{uri: this.movie.posters.thumbnail}}
style={styles.thumbnail}/>
[/code]
Um eine Anwendung zu layouten, implementiert ReactNative den Web-Standard Flexbox. Da Facebook Flexbox für native Anwendungen neu implementieren musste, wird nur ein Subset unterstützt. Das Verwenden von Flexbox ist sehr viel bequemer, als selbst AutoLayout unter iOS zu nutzen.
Zusammenfassung
React Native überträgt das Programmiermodell moderner Webapplikationen auf native Apps: Learn once, write everywhere.
Mit React Native bekommt man also das Beste aus zwei Welten: die saubere Struktur von React-Komponenten und den schnellen Entwicklungszyklus von Web-Anwendungen gepaart mit der Performance von nativen Anwendungen. React Native verfolgt nicht den „write once – run anywhere“-Ansatz von Java oder dem Cross-Plattform-Ansatz von Frameworks wie Appcelerator Titanium. Für jede Plattform kann eine eigene App entwickelt werden. Allerdings ist die Developer Experience über beide Plattformen (iOS und Android) gleich. Windows wird als zusätzliche Plattform in der Zukunft unterstützt werden.
React Native eignet sich, wenn Entwickler bereits Erfahrungen in React gesammelt haben und eine zusätzliche native Applikation bereitstellen müssen. Natürlich ist man am flexibelsten, wenn man direkt nativ programmiert, aber als Alternative zu Cross-Plattform oder hybriden Ansätzen sollte man einen Blick auf React Native riskieren.