React Native: Does Universal Code Really Work?

July 19, 2016
VP, Product Engineering

React Native is a new mobile programming framework for application developers based on JavaScript and React. With React Native, developers can use universal code across a wide variety of native platforms. React Native aims to increase developer efficiency using the motto “learn once, write anywhere.” Facebook is the original driving force behind React Native, using it for multiple production apps, but more recently, many other companies have jumped behind React Native. When Microsoft and Samsung joined the conversation, they contributed to React Native’s upward trajectory. It is now being used by more than 500 companies and developers who publish apps to the Apple app store and more than 200 companies and developers who publish apps to Google’s Play store. 

Why is React Native so popular?

Rect Native utilizes the “virtual dom” concept to allow developers to write JavaScript code and allow the library to manipulate native components when a change in state has occurred. This means that purely native code still has an edge in performance. However, React Native’s versatility and ease of use appeal to a wide variety of audiences. Because it’s based on JavaScript, it can be used with a number of platforms without having to alter any platform-specific code. This offers a huge discount in the time developers have to spend on each project, which is both attractive to individual workers and companies paying for the labor. In addition, React Native eliminates CSS, includes native components for a true platform feel, and allows for hot/live reload.

When should you use React Native?

The client’s goals are what largely determine the platform choice. React Native is better for projects when the time-to-market is critical, the client wants to keep maintenance costs low, or overall performance is important. The versatility of code helps to keep maintenance at a minimum, allowing less experienced individuals to manage. React Native also comes into play when there is a desire to leverage existing front-end development knowledge because it looks good and has a number of platform benefits that appeal to clients and users alike. 

However, there are many times when other platforms are a better fit for the situation. For example, you should use native when only one platform is needed, there is a budget to pay for separate Android and iOS development teams, and performance is critical in each and every individual aspect of the application. Most importantly, when React Native doesn’t have what you want and you are not willing to build it, native is your go-to, backup platform. Ionic or other HTML5/Cordova options are better suited for clients who are not concerned with performance or if the app looks native and they can leverage Angular skills to get to market quickly.

What does React Native look like in action?

React, and therefore React Native, leverages JSX to build the UI. JSX looks a whole lot like HTML, but in the case of React Native, the tags are elements that should be familiar to native developers. For example, when you want to include text on the screen, use a <Text> element or for an image (you guessed it), <Image>.

Most (though certainly not all) React Native examples are written using ES2015 syntax, which can be a slight hurdle for those developers who are accustomed to writing ES5 code. Each component is, therefore, an ES2015 module. You can then compose these components into screens. There is really nothing special about a screen vs. any other component, but I find it very helpful to separate the two during the flow of development.

A component might look something like:

'use strict';

import React from 'react'
import { View, TextInput, StyleSheet } from 'react-native'
import Icon from '../components/Icon'

class SearchBar extends React.Component {
  render() {
    return (
      <View style={styles.searchBar}>
        {this.props.leftIcon &&
        <Icon wrapperStyle={styles.iconBorder}
                    size={32}
                    imageName={this.props.leftIcon}
                    onPress={this.props.onLeftPress}/>
        }
        <View style={styles.textInputView}>
          <View style={styles.wrapperForAndroid}>
            <TextInput
              ref="textInput"
              style={styles.textInput}
              placeholder="Business name, keyword ..."
              placeholderTextColor="white"
              value={this.props.searchTerm}
              onChangeText={(text) => this.props.onSearch(text)}
            />
          </View>
        </View>
      </View>
    );
  }
}

SearchBar.propTypes = {
  onSearch: React.PropTypes.func.isRequired,
  searchTerm: React.PropTypes.string,
  leftIcon: React.PropTypes.string,
  onLeftPress: React.PropTypes.func
};

var styles = StyleSheet.create({
  searchBar: {
    backgroundColor: '#3e5667',
    flexDirection: 'row'
  },
  iconBorder: {
    padding: 6.5
  },
  textInputView: {
    backgroundColor: '#03202F',
    alignSelf: 'stretch',
    margin: 5,
    flex: 1
  },
  wrapperForAndroid: {
    height: 32,
    marginLeft: 5,
    marginTop: 2,
    marginBottom: 2,
    flex: 1,
    justifyContent: 'center'
  },
  textInput: {
    fontSize: 16,
    height: 32,
    color: '#FFFFFF',
    flex: 1,
    paddingTop: 4,
    paddingBottom: 4
  }
});

export default SearchBar;

Let’s break that down a little bit. We start by importing the base level classes from React and React Native respectively. Here we also import the Icon component.

Then a class is created that extends React.component. There are several lifecycle functions that can be implemented, but the one function that is required is render. The render function must return a JSX component. This render function is called by React whenever the state or props are changed. To utilize the state or props within your components, simply include the JavaScript inside of curly braces. For example: imageName={this.props.leftIcon} sets the imageName property on the Icon component to the value of the lefticon prop that was passed into this component. 

Once the class is created, it's time to define propTypes. These tell React what properties can and/or must be passed into the component and serve two purposes. First and foremost they are the documentation. When using a component inside another component, you are able to quickly look at the component propTypes to determine what types they are and what is needed to pass. Add .isRequired to the end of any prop to make it required. The second function is that propTypes are checked in the development server at runtime and a yellow warning box notifies you if there is a mismatch. This is a tremendously useful debugging tool. 

The last piece of code here is the styles. These are defined by calling StyleSheet.create and passing an object that looks a whole lot like CSS. As the name implies, it is really the SS without the C. All styles are defined right here with your component. Everything is laid out utilizing FlexBox.

The module file is ended with a default export of the class that was just created.

The fact that everything is self-contained inside this one file makes laying out the screens very easy relative to wading through large CSS files.

Development deployment

Now that we have some code, let's view it on a device or simulator. Head over to your terminal and run: react-native run-ios. This starts up an npm server and your iOS simulator with your code running.

Live reloading

Live Reloading is by far the single killer feature of React Native. In fact, it is so great a feature that I would strongly consider using React Native over actual native development for the benefits of Live Reloading (even if I were only building iOS). Just shake your device (cmd+option+z on the simulator) and select Enable Live Reloading. Then modify some code and bam! Your application is updated in place. No refresh necessary. No going back to the beginning of your app flow. None of the craziness and slowness that comes along with native development. Now that's what I call efficiency and yes, universal code really does work!