Im September 2016 kam bei meinen Arbeitgeber die Frage auf, mit welchen Mitteln würde man die nächste App angehen. Produktiv haben wir erfolgreich eine Ember-basierende App und eine Meteor-basierende App. Was sagen andere die Erfahrungen mit den Techs gemacht haben, würden die dieses wieder verwenden? Wie bekommt man das “Native App Erlebnis” aka 60fps hin?
In einen ersten Schritt habe ich auf den einschlägigen Kanälen nach Expertenmeinungen gesucht und zusammengetragen im Dokument “Stand der Web Apps: Native vs. Hybrid vs. Mobile” (PDF).
Daraus kristalisierte sich, dass React Native ein zwar junges aber doch schon dank massivem Support durch Facebook gereiftes Projekt ist. Um mittelfristig eine fundierte Entscheidung treffen zu können, war eine Analyse der Möglichkeiten des Ökosystems unausweichlich.
Die beste Erfahrung macht man indem man eine App wirklich programmiert. Ich kann ein Buch zu NodeJS lesen, aber wenn ich die ersten Zeilen programmiere, dann bekommt man erst mit, was es heißt in der Callback-Hölle zu landen. Da kamen mir Pilzdaten meines Vaters ganz gelegen. Listendarstellung, Detailansicht, Bilder, Merkliste, Datenhaltung mit Redux - wird alles benötigt, ist dann eine echte App. Anhand dieses mittelkleinen Projektes habe ich mich dann durch die verschiedenen Facetten gehangelt und recherchiert.
Am Ende entstand ein Vortrag, den ich im November/Dezember bei meinen Arbeitgeber und auch im Januar bei DresdenJS gehalten habe - in verschiedenen Kontexten natürlich. Zudem ist mittlerweile für CodeforChemnitz der Theaterwecker als App in der Entwicklung.
Folien zum Vortrag “Native App Entwicklung auf Pilzen” - in drei Teilen:
- React Native: Motivation, Pilzprojekt, Ökosystem
- React: Entstehung, Komponenten, Stylesheet
- Redux: Paradigmen, Suche und Trefferliste, Prefill
Projekte:
Nachfolgend sind noch die ganzen Recherchen gelistet - hauptsächlich auch für mich selbst zum nachschauen.
Einstieg, Kernidee, Dokumentation, Quellen
- Use JavaScript as the language doesn’t have a long compilation cycle time.
- Implement a tool called Packager that transforms es6/flow/jsx files into normal JavaScript that the VM can understand. It was designed as a server that keeps intermediate state in memory to enable fast incremental changes and uses multiple cores.
- Build a feature called Live Reload that reloads the app on save.
React und seine Alternativen
Die Besonderheit liegt darin, mittels der Komponenten HTML in JavaScript integrieren zu wollen, statt andersherum. Das kommt einerseits der Übersichtlichkeit des Codes zugute und andererseits lassen sich dadurch von Hand durchgeführte DOM-Manipulationen vermeiden, an denen bspw. Web Components und AngularJS weiterhin kranken (etwa im Fall von Data Binding).
UIExplorer (per Playstore verfügbar!)
Getting Started
- Follow the Getting Started guide to install React Native and its dependencies.
- Check out this tutorial to walk through your first project that fetches real data and displays it in a list.
- Open the UIExplorer example project to see a list of components that ship with React Native.
- Install the React Developer Tools for Chrome or Firefox for better debugging (read more).
- Try out apps from the Showcase to see what React Native is capable of!
React Native TODO Example
- ReactNative-Redux-Todo-List basiert noch auf React 0.14.3 - aktuell ist >0.33.0 (npm Package)
- Redux ExampleTodoList
- React Native UI Elements
- und App die alle Elemente zeigt - Hackathon Starter
- Farben & Farbnamen
rnpm - React Native Package Manager (deprecated) braucht man nicht mehr, wurde in React Native Core übernommen
Props vs. State - Component API
- Doku: http://www.reactnativeexpress.com/component_api
- props sind schreibgeschützt und nicht für den momentanen änderbaren Zustand einer Komponente geeignet
- state niemals direkt ändern mit
this.state.bla=2
sondern immer überthis.setState
- damit rendering angestoßen wird
this.props
Components can be configured upon instantiation by passing properties to the constructor - these properties are called props. props may be accessed from within the component’s methods as this.props, in order to alter how the component is rendered and/or how it behaves. However, props must not be altered from within the component’s methods. A parent element may alter a child element’s props at any time. The child element will generally re-render itself to reflect its new configuration parameters. A child component may decide not to re-render itself even though its configuration has changed, as determined by shouldComponentUpdate() (more on this in the Component Lifecycle API section).
this.state
Components may maintain their state internally within the object state. this.state may be accessed from within component methods. Unlike props, parent elements may not access a child’s state, as it is intended to manage the child’s internal state rather than external configuration. Note that you should never directly assign to a specific key within the state object, e.g. this.state.foo = ‘bar’, but instead use the method this.setState().
Lifecycle API
http://www.reactnativeexpress.com/lifecycle_api
Components have a lifecycle: they are instantiated, mounted, rendered, and eventually updated, unmounted, and destroyed. The lifecycle helps manage the complexity of different platform APIs (iOS, Android) by providing a simple, consistent abstraction layer. The lifecycle also allows you to optionally execute custom code at each step for more fine-grained control of the rendering.
Bei JS-Fehler in iOS App kann per “JS Reload” im Fuss das JS neu geladen werden - ohne Rebuild
state in constructor setzen vs. getInitialState
Ask: What is the difference between using constructor vs getInitialState in React / React Native?
this.state
in constructor direkt bei ES6 Klassen, getIintialState bei ES5
Debugging
Debugging aktivieren
- Doku
- auf iOS -> Cmd+D
- auf Android -> Cmd+M
A desktop app for inspecting your React JS and React Native projects.
The standalone app based on official debugger of React Native, and includes React Inspector / Redux DevTools
Stetho is a debug bridge for Android applications, enabling the powerful Chrome Developer Tools and much more.
Upgrade RN (via Git)
react-native-git-upgrade
benutzt Breaking Changes aus Wiki https://github.com/facebook/react-native/wiki/Breaking-Changes
Redux
Reducer sind Pure Funktionen, dürfen übergebenen state nicht ändern, sondern ein neues Object
this.state + setState an React Komponenten kann komplett entfernt werden, da Zustand von Redux über Props eingespült wird
Eventhandler kommen auch per Props rein, z.B. onRemoveSmth
Prior Art: basiert auf Flux (Facebooks State für React), Elm (“model view update” Architektur), Immutable (persistene Datenstruktur, Undo History!)), Baobab, Rx (u.a. Observables)
combineReducers
und loadStore
mit preloadedState
- Ask: Read Store’s Initial State in Redux Reducer
- Commit in pilzliste-react-native-redux
- Updates in Redux-Connector verändern Props ->
componentWillReceiveProps
statt auf State-Änderung hoffen..
Memoized Selectors um mapStateToProps bei großen Datenmengen nur mit den Änderungen zu bedienen
Production Build Android
Signaturfile erzeugen:
keytool -genkey -v -keystore my-keystore.keystore -alias name_alias -keyalg RSA -validity 10000
Android Build:
./gradlew assembleRelease
jarsigner -verbose -keystore my-keystore.keystore app/build/outputs/apk/app-release-unsigned.apk name_alias
adb install app/build/outputs/apk/app-release-unsigned.apk
Build beschleunigen -> Gradle Daemon aktivieren
in ./android/gradle.properties
-> org.gradle.daemon=true
Android Emulator (AVD) von Kommandozeile starten
/usr/local/bin$ ln -s $ANDROID_HOME/tools/emulator
emulator -list-avds
emulator -avd Nexus_5_API_23_x86
Fehler “Qt library not found at /usr/local/bin/lib64/qt/lib”? Antwort auf SO
brew install qemu
es bleiben Fehler :(
brew remove qemu
/usr/local/bin$ ln -s $ANDROID_HOME/tools/android
android avd
:)
ListView Performance
Lazy Load
- Große Datenmengen führen im verwendeten RN-Componentes List und ListView zu langen Ladezeiten
- Lazyload baut Views und Inhalte bei Bedarf aus State-Daten
- [react-native-lazyload](https://www.npmjs.com/package/react-native-lazyload()
- Commit in pilzliste-react-native-redux
Offizielle Performance-Hinweise
- Performance
- ListView statt ScrollView!
Layout
Image
-
Image-Komponente (Doku)
- kann nur PNG, keine JPG - lolwut?
- Ask: React Native can’t load image
Fehler “The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.”
- Meteor issue “App Transport Security support aka apps on iOS 9 don’t work”
- RN issue “xcode 7.0beta could not connect to development server.”
- RN issue “[Image] Example issue: ImageView not loading image”
- Lösung 1: in
info.plist
,NSAppTransportSecurity NSAllowsArbitraryLoads YES
- Lösung 2: https aktivieren für Domain und statt http verwenden
Pilzbilder kleinrechnen
- CLI options für IM resize
- xargs
- in
httpdocs/original
:find . -type f -name "*.jpg" -print0 | xargs -0 -I {} -P 4 convert {} -thumbnail "60x60" -strip ../thumbnails/{}.png
Navigator / Router
- Routes wenkesj/react-native-routes
- Navbar react-native-navbar
- WIX Navigation wix/react-native-navigation
- Offizieller Router: react-native-router-flux
Blog-Posts:
Ein neuer Router ist aktuell auf der Roadmap als Alternative zu Navigator, NavigationExperimental, und ExNavigation.
TouchableHighlight
Tap auf ein Element, highlightet es und führt eine Aktion durch. (Doku)
View display:none, visbility:hidden? height:0 / destroy Issue
Iteration über Elemente mit Child components
benötigt immer key als Attribut
{ this.state.items.map((item, i) => (
<ListeItem key={i} item={item} />
)) }
Fuzzy-Suche
- mattyork/fuzzy -> zu langsam
- Commit in pilzliste-react-native-redux
- ggf. Bloodhound mal probieren
- Redux Suche
Debounce TextInput
Images mit Ladebalken
Images cachen
- reactnativecn/react-native-http-cache -> schwierig
- remobile/react-native-cache-image -> veraltet
- mit Realm-Backend
- nutzt react-native-fs
- Setup Android: MainApplication.java anpassen
- Setup iOS: brew install cocoapods; ./ios$ pod init; edit Podfile; pod install
- CocoaPods: dependency manager for Swift (and ObjC) -> https://cocoapods.org/
- react-native-fs geht gerade 2.0.0-RC und deprecated
- CocoaPods React ist deprecated und 0.13.0
-
erstmal in eigenen Branch gepackt
- react-native-ximage deprecated
-
React >= 0.25.0 kann prefetch und caching (Commit)
- Prefetch nur einmal? Ja! Apache Log prüfen:
tail /var/www/vhosts/system/uli.rh-flow.de/logs/access_ssl_log
- (Commit)
- Testen mit iOS Simulator - schwierig, gibt keinen Offline Modus
- “Turn off your network connection on the Mac.”
Offline first
App Intro (erste Bedienhilfe)
Popups, Dialoge
React Parts
- React Parts (deprecated)
-
Liste auf JS.coach
- Kamera react-native-camera
- HTTP Cache react-native-http-cache
- Google Analytics react-native-google-analytics
-
CameraRoll rn-camera-roll
- Splash Screen in RN 0.40
RN Plattformen
Android, iOS
macOS Desktop Apps react-native-macos
Windows UWP (Universal Windows Platform) react-native-windows (aktiver als MacOS!)
Web
react-native-web
(Grundlage für App-Demos in offizieller Doku)
Starter
Stylesheets
react-with-styles und react-with-styles-interface-react-native
Libs
Design Pakete
-
NativeBase ($50)
- Grundlagen-Design damit es auf Android wie Android und iOS wie iOS ausschaut
- Flat Design Pack ($100)
- Login/Logout Animationen ($35)
- Material Design Kit
- Shoutem
Organisation
How to better organize your React applications
Storybook
Bootstrapping UI: Storybook react-storybook getstorybook.io