Learn Once, Write Anywhere.

That’s what they say right, the React folks. Well, I learned a little bit of React, enough to be dangerous at least, and now I am ready to see if what they say is true.

This story focuses on building a simple iOS app. The experiment will try and answer the question, “If I know React and Javascript, can I build a native app using React Native easily?”

My Background

I have used three modern web frameworks by now including React/Redux, Angular, and Ember CLI. Today at Points, I write Javascript using ES6 syntax in all our front end apps and continue to invest in the pattern of React Components, Uni-directional data flow and managing immutable data in a single store. I don’t have any native development experience though and am coming at this experiment as a complete beginner.

Getting Started

The react-native Hello World, much like the real world, can be a bit brittle. Just installing/updating Homebrew and depending on your OS, you can encounter some pretty cryptic errors. It’s not worth describing the details of my experience in this story but I sense signs of productivity loss in the near future with some of these tools (crashes, vague errors, incompatibilities, lack of documentation). Nevertheless, use Google, try some things, and eventually you will have your system up and running. I was able to do it fairly quickly.

A Default App

Once you have installed all the necessary packages and are able to run your app in xcode, this is what you get right out of the gates. Already, if you have even a little bit of React experience you can see right away how it approaches the concept of Learn Once Write Anywhere (LOWA). I see my dependent modules and Components being imported, an ES6 class extending React Component and a render method, letting me know I can also use the other React lifecycle hooks.

Notable Concepts

Flexbox

React Native uses a layout model based on flexbox. It takes some getting used to and is only a subset of it’s css counterpart of which it was derived. By default, every component has a display property of flex and the flexDirection property is set to column (meaning sibling views are stacked). Generally you want to give Views a style of flex: 1 which will ensure your component will take up 100% of the screen. This article comes up a lot in Google searches to help people understand the layout system

View

Views seem to be the equivalent of a <div> in html/css land. You usually want a style of flex: 1 on them so they can allow nested views to scroll.

Lists

the items in a list are initialized as a new ListView.DataSource

this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
})
};
}

You can then hydrate that list by using the cloneWithRows method on the DataSource instance

this.setState({
dataSource: this.state.dataSource.cloneWithRows([ {...}, ...])
});

Image Width and Height

It’s nothing like you would expect coming from the web. I have seen a lot of discussion around the web on this subject on StackOverflow and Github issues about this behavior. In order to get an image to stretch the width of your screen, your container style looks something like this:

container: {
flex: 1,
alignItems: 'stretch'
},

image: {
height: 200
},

Stretch makes the image stretch but without a height attribute the image simple doesn’t show up. This is frustrating for web developers because browsers inherently maintains an image’s aspect ratio when setting one dimension, for example width: 100%

There are a few fixes for this I have found and a very lengthy discussion on Github about it, but since this experiment is meant to just test the waters I have yet to untangle the solution. Also, here is the React Native team’s explanation on the topic

If you find your images are being cropped, take a look at the resizeMode prop in the Image Component. Using contain or cover should fix those issues.

Touch Events

The TouchableHighlight component is a basic component to handle touch events but it is kind of tricky. I was getting errors using it in my list view because the value of this was undefined with defining the onPress props that was to handle the event. The solution was to bind my touch functions in the constructor like this:

constructor(props) {
//...

this.renderLp = this.renderLp.bind(this);
this._onPressLp = this._onPressLp.bind(this);
}

this.renderLp is the function that renders each row of the list and the onPress function is of course the touch callback.

Debugging

Debugging is surprisingly easy using Chrome. All you need to do is type command + d in the simulator window and select debug in chrome from the menu. it will open a new Chrome tab where you can use dev tools to set breakpoints and use the console.

One thing to note is the React Dev Tools plugin for Chrome does not work with React Native, which is a bummer because it is such a powerful tool. See the explanation here

My Reactions

All said and done I think it’s very exciting that I could “throw together” an app in just a few days leveraging my knowledge of Javascript and React alone.

Based on the app I have created, I believe it is entirely possible to get a simple app off the ground using React Native. I also believe the available npm packages are great for supplementing what is supplied out of the box to help create various user experiences.

The documentation definitely needs some work. it is unnecessarily verbose in certain areas but lacks sufficient examples, and when there are examples, they can can be long and convoluted. It should be more concise and clear on usage with short examples to illustrate certain points. I have to say the Ember team has done a really good job on developer guides and documentation

After completing this experiment I can truly see the value in this endeavor and am grateful the folks at Facebook are committed to this project. I still haven’t touched on many other concepts like Running on Device, or working with some of the Native APIS which I am really excited about.

So go on, get after it, make an app. If Kanye & Kim can do it then you can too, obvi.