I spend a few days writing my first Android and iOS app with React Native (I'm an experienced mobile developer). I didn't want to write a demo app that does just the basics, and I wanted to explore the things that could be problematic with React Native:
- The app must feel like any native app: smooth and looks like native on both iOS and Android. For example: a toolbar (with text and icon buttons), respecting material design margins, transition between screens, etc.
- Easy way to access local storage (sqlite for instance).
- Handle push notifications.
- Make sure the tools are good: IDE, checkstyle, linter, debugger, etc.
- Generated app size.
The app I wrote is totally useless (it's a mix of navigable screens via tabs and stacks, with lists, buttons, icons, images, cards, etc).
The awesome
- React components. React components are really powerful, especially when you compare this with the traditional way of grouping views in Android (aka. Fragments). Really nice to separate purely visual components and components that handle the logic (aka dumb and smart components). Great to compose bigger components from atomic components.
- React ecosystem. Especially Redux. I'm not familiar with JS and the setup was not easy. Redux's store maintains the app state and it's interesting for mobile because it can also be used for navigation. If your store is well structured, you won't have to think about things like activities going to the background or device rotations. There are plenty of redux middlewares that allow you to do complex things by just adding a few lines of code.
- Hot reloading. It works and it's fast. Save your source file and ~200ms second later you can see the change on your emulator / device.
The good
- Modern Javascript. I remember when I tried to start a react native app ~2 years ago, I was fighting with JS to write a simple class or export a function. It's solved in ES6, and the syntax is clean. It's not the only thing coming from ES6 and ES7, but this simple thing made my life much easier.
- Flexbox. I like it, never had any issue to create something I wanted, I was a bit confused with
alignItems
but that's the kind of thing you learn by using the tools. - Type checking. I discovered flow after reading some examples. It allows you to type check your JS code. Coming from Java/Kotlin/ObjC/Swift development, this is pretty nice to have. (Typescript is another alternative and seems pretty easy to setup).
- NativeBase. It's a set of native components (built on top of the official components), that allow to write themeable cross platform apps. Some nice utilities as well (includes thousands of icons for example). I used the Card, Button and Checkbox components, they look like native can be mixed with "standard" components. This is why I can say my app looks like a native app. That wouldn't be the case if I only used the core components.
- Adding new dependencies. It's actually easier than in an Android project. Example to add the react-native-fs dependency:
# Add the JS dependency:
$ npm install react-native-fs --save
# Link the native module in the iOS and Android projects:
$ react-native link react-native-fs
- Expo.io. You have an iOS device, but you don't have a Mac or you don't want to install XCode? Expo allows you to write basic React Native code (ie. without external native modules). A bunch of utilities are bundled with the library so you shouldn't have to install external native modules for a basic app. You can even do some basic experiments from your browser, thanks to Expo Snack.
- Local Storage. There is a core component called AsyncStorage, it's a key-value storage system. I think it's enough for most apps, and if you really need to sort, filter, search and do other operations in the data in your app, there is also an external library that wraps sqlite using the same Javascript API for iOS and Android.
- Apk size. Final apk size of the app is 9Mb, which is not small for a simple app with almost no assets, but not huge. Definitely in the "normal" zone. Most of it comes from
./apk/lib/x86
and./apk/lib/armeabi-v7a
(native libraries including the Javascript interpreterlibjsc.so
, the stl, Fresco, some RN jni code). - Developer tools. It's a good suprise, but you can use a lot of the Chrome dev tools plus the react-devtools, but also redux-devtools to track and edit your app state.
- A whole new world to explore. I didn't have time to explore, but I read some articles about some super exciting tools and libraries like: react-apollo, CodePush, React Native for Desktop (OS X, Windows)
The bad
- Navigation. It's a super easy task in native Android and iOS, developers don't even have to think about it (unless you have to implement deeplinks). But in React Native, it's not that easy. And to make it even more complex, they have an history of writing the same kind of library 3 times and give them similar names. After reading recent blog posts about it, it seems there are two viable alternatives: React Navigation (it's only ~6 months old) and Wix React Native Navigation. I wrote my app with React Navigation, things were going pretty well, until at some point I just wanted to replace the top of my StackNavigator by a new screen instead of poping/pushing a new screen. After some dirty route hacking, I was able to replace the top screen, but I also lost my transition animation.
- Documentation. The official documentation is pretty shallow (example there is no explanation about linking to a local image, but if you read the unit tests, you can actually find some examples). Community documentation (ie. blog posts from react native devs) is also hard to follow. Because things are changing very fast, you often read an outdated blog post that could actually derail your understanding of a certain topic. Things are moving fast, if you wrote a React Native app 1 year ago, you probably had to maintain and upgrade it to recent a recent React Native version. I don't have any experience on this, but I guess it's not always trivial and it's probably time consuming.
- Push notifications. The core API allows to handle push notifications but only for iOS (see
PushNotificationIOS
). I actually found a library react-native-push-notification that implements it for Android and wrap the corePushNotificationIOS
, I'm not sure why this is not part of the core modules. - Steep learning curve. It's definitely not easy, I'm just starting (I had some JS experiences before). I was pretty discouraged when I started because I couldn't just write my app, I was just struggling with some stupid problems (see notes below).
The ugly
- Access filesystem. Download a file and read it from the app? Put data into a your
assets/
folder and read it from the app? It's not standard, but there are workarounds (like storing your data in.json
file and package/require it with your React app or using an external library). That's actually why I ejected my app (and I then noticed there are some methods in Expo to access the filesystem). To me it's a big missing piece from core react native. - Threading. There are only 2 threads: the UI thread (where native layout happens) and the JS thread (where the JS code is executed, it syncs with the UI thread regularly). This can lead to some performance issues if your JS thread is doing heavy work and the UI can't sync up. You can also write native modules that spawns new threads and do other stuff there, that might be a requirement for things like image manipulation, you probably won't do it in pure JS anyway.
Notes
- Ejecting my app. I used a cli utility to bootstrap my app, it's called create-react-native-app or CRNA. Took me some time to figure out that was an Expo.io app. I was trying to add a native module and my app structure was different from the examples and docs I was reading. I finally stumbled upon that blog post and was pretty happy that I could just run
npm run eject
and get my native code compilation process back (ie. iOS and Android project structures). IMO, the naming of CRNA is pretty confusing. - Accessing local images. I've put app's local data in a JS file, that looks like:
data = {title: "My Title", image: "assets/local_image.jpg"}
and then I wanted to use that image in one of my component by doing something like:<Image source={uri: this.props.data.image}>
- I finally found it was supported by reading this test in react native unit tests. For some reasons, I have to use the Android id (without the extension) and the filename on iOS. This is still an open issue, I guess the next step is to read the
Image
code. - Navigation, screen replacement. I wanted to replace the top screen of my
StackNavigator
. Pushing / Popping screens is pretty easy to handle, but there is no way to replace the top screen of aStackNavigator
without hacking the router. I did so, but I don't have any transition animation anymore, so this is still an open issue. Maybe my navigation structure is incorrect. This is the kind of thing you learn by experience. Anyway, it's a pretty standard use case, and I'm not the only one interested by this feature.
Tips
- Use Visual Studio Code with the React Native extension, it's really great. I also tried DecoIDE but VSC is so much better. I couldn't stick with DecoIDE (can be great for learning the component parameters).
- A doubt about a specific component? read the tests.
- Source of NativeBase kitchen sink app is also useful if you use NativeBase.
- If you don't know anything about React and Redux, read the source code of a very simple demo app to understand the flow (which part call which part, where the data is stored and accessed, etc.)
- Curated list of articles, tools and libraries: Awesome React Native.
Learn
- React Native Express. A really good way to start with the basics of (modern) JS, React, React Native, Flexbox, Redux.
- Getting started with React Native (official doc).
Conclusions
I was able to implement everything I wanted, the toolset is great, the app is smooth, looks like any Android native app (the app I wrote is cross platform but follows material design specifications). I'm a bit worried about relying on external dependencies for basic things like push notifications and accessing the file system. And I'm really worried about the navigation (I will try other navigation libraries). Someone with almost no knowledge of React and the JS world will probably spend most of his time struggling with the basics and being stuck on some simple problems that could be solved in seconds by using the native sdk. But someone with a good knowledge of React Native can write a semi-cross-platform app that looks like a purely native app in a short amount of time (compared to write 2 pure native apps), especially if that app can be built on atomic components. I'm really pissed off by the Android Activity + Fragment lifecycle, so having another way of building an app that removes this pain is nice to see (I'm aware of some alternatives Scoop, Flow+Mortar, but never tried them). If I had to start an app from scratch targeting iOS and Android, I'd definitely start by doing a react native experiment and try to implement what could be the biggest pain point.
Next
I'd like to try Weex (a Vue.js equivalent for mobile apps) and I'd also like to try wrapping a native library (for iOS and Android) as a react native module.