My adventures with React Native

I was incredibly excited watching the ReactConf video when they announced React Native. After many frustrating attempts to create great user experiences with PhoneGap, I had started to believe that the dream of using web technologies for mobile apps was dead. I’ve been using React on the web heavily for the past year and ever since I first grokked the “react” way of doing things, I felt like this was the future of web development. And now after using React Native for a full project, I have the same feeling that React Native is the future of mobile app development. I don’t think it will replace Objective-C or Swift. There will always be things that are better done native than through javascript. But React’s method of building UI’s is the future. I think React Native is the perfect example of the Hybrid App model. Use native Objective-C for what it’s good at, and web technologies for what they’re good at.

The Challenge

Could one designer and one developer make a mobile app in a weekend. (tl; dr: YES!)

And it’s all open-sourced: github.com/asamiller/den

If you’ve ever looked at buying a house in Oregon or southern Washington, undoubtedly you experienced an out of date, clunky website to search available listings. We decided to build a better experience for browsing homes listed on rmls.com.

Getting Started

The React team has done a great job of making React Native easy to get started. Their CLI creates the project scaffolding for you and then hitting Build in Xcode launches the compiler server automatically and you have a native app running in the simulator! Hitting CMD+R is amazing. No more waiting for Xcode to build. I have an iOS developer friend who, a few years ago, dropped 20K on a new top-of-the-line 12 core MacPro because it cut his build time in half (from 6 minutes to 3) and he figured it would pay for itself easily in increased productivity. Being able to hit CMD+R is worth all the cores you could ever fit in a MacPro.

React on the web vs. React Native

Developing with React Native is very different then developing for the web. The way iOS works is fundamentally different than the way the web/DOM works. Hopefully we can all agree that we’re done with the false idol of “Write once, run everywhere”. Each platform (iOS, Android, Web) that you want to support requires a rethinking and separate build. That being said, there can be shared code across them but more importantly, as the React team has said, the skills that you have can be reused.

<View>

Views in iOS, in some ways, are similar to divs on the web. They can be used to wrap and group other elements and they can be styled directly. But just as divs shouldn’t be overused on the web, don’t go crazy with Views either. In some cases, there are native iOS components that are better suited to the platform than plain Views.

<ListView>

ListViews are used when you want a potentially infinite scrolling list. Pretty much anytime there’s repeating content. This is much better than doing a loop and drawing a bunch of Views because iOS is very smart about only loading and drawing the rows that it needs. That’s how you get silky smooth scrolling even if you have hundreds of rows.

<NavigatorIOS />

There’s lots of ways to go to another “screen” in single page apps on the web. You could use react-router or just toggle divs on and off. On iOS there is a native system method of going between screens. It handles screen transitions and only drawing what’s necessary. It’s a push-pop stack and can be thought of like an array: [screen1, screen2, screen3]. You can push a new component to it and that will be the current view, or you can pop the last item off, to go back a screen.

To use the iOS navigator, wrap your component in the NavigatorIOS component. The initialRoute properties takes the title and component for the first screen you want to show.

var MyFirstScreen = require('./MyFirstScreen.js');
<NavigatorIOS initialRoute={{
title: ‘My First Screen’,
component: MyFirstScreen
}} />

When we want to go to a new screen we can push a new component to the stack. Inside MyFirstScreen you have access to this.props.navigator.

this.props.navigator.push({
title: ‘My Second Screen’,
component: MySecondScreen
});

There’s lots more on the React Native docs.

Flexbox

I have yet to meet anyone who says they really understands Flexbox but I’ve learned a lot through trial and error (thank god for cmd+r) with React Native. I found what I know about laying out content for the web didn’t really help on native. Properties like margin and padding work as expected, but there’s no inline-block or floats. Instead Flexbox is used anytime content needs to be side by side.

Drawing the icons and text side-by-side took me longer than I care to admit. This is obviously a pretty simple thing to do on the web, but figuring out where to put flex: 1 and where to put flexDirection: ‘row’ took some trial and error. In short, here’s what I learned.

flexDirection: ‘row’ tells the View to draw it’s children using Flexbox horizontally.

flex: N is a property set on the View’s children that tells Flexbox how much space to take up. For example if every child element has flex: 1, they will all be the same width. Say you have 2 children. If one child is flex: 2 and the other is flex: 1, the flex: 2 will take up twice the space. If you add up all your flex values (in that example; 3) then the flex value is how much of the over all value that view takes up.

At first I started putting flex:1 on everything… long story short, that screws everything up. flexDirection should be on containers and flex should be on children.

Images

There are two ways to use an image with React Native. Both use the <Image> element. One loads the image from the web, the other locally from the app.

To have it load the image from the web, set the uri property to the path of the image.

 <Image source={{uri: ‘http://domain.com/image.jpg’}} />

To load it from the local app bundle, you use require(). This seems a little odd since we’re used to using require to load javascript, but the React team has some good reasons for doing so. You want to require just the file name before the resolution (@2x) or extension (.jpg). So for an iOS image I should have: puppy.jpg, puppy@2x.jpg and puppy@3x.jpg.

<Image source={require(‘image!puppy’)} />

The system is smart enough to pick the correct resolution version for each device it’s used on.

But there’s one more thing you have to do; add the image to your Xcode project and rebuild it.

Drag and drop the image onto the Images.xcassets icon inside your Xcode project. This is an bundle of images that Xcode includes with the app when it’s built. You’ll need to hit run again so the image are available in the simulator.

Wrapping up

React Native is a fantastic way to build mobile UIs. Just like learning React on the web, it took some getting use to it, but once I did, there’s no going back!