Over the past few days, I’ve had the opportunity to build a mobile application for both Android and iOS mobile devices. Constrained by time, and being familiar with React JS, I decided to implement the applications using React Native.
React Native is a JavaScript library authored by Facebook and released to the open-source community in the Spring of 2015.
Facebook was motivated to develop the library because they found their mobile development efforts were, at times, much slower than web development. The premise was to design a library which could be used with two very different development platforms, Android, and iOS.
React Native, in turn, allows engineers at MojoTech to develop cross-platform mobile applications with “Learn once, write anywhere” efficiency.
This post explores some of the ways in which React Native differs from React JS. Getting a mobile app up and running, in general, has its own learning curve. Fortunately, the React Native documentation details the tooling process, so I’ll limit dev-tool comments to important points and a couple of gotchas worth mentioning again.
5 Differences
(Ordered by workflow)
1 - Setup
Setting up the project from the command line makes life easy. The following two commands set up all the files needed for both Android and iOS deployment.
$ npm install -g react-native-cli$ npm init react-native <YOUR_PROJECT_NAME>
To keep things organized I did set up a folder structure to house the application code: from the application root run
mkdir -p App/Components && cd App
2 - Components
React Native uses pre-defined Components. Rather than
render(){return(<ScrollView style={styles.content}><Text style={styles.heading}>Add Any Label Information</Text><View style={styles.bordered}><Text style={styles.listing}>1. Gauge - Gauge Units</Text><SegmentedControlspaddingTop={10}paddingBottom={10}containerBorderTint='#c1a7d3'tint="#5d4c8a"backtint='white'options={['inches', 'centimeters']}onSelection={this.setGaugeOption.bind(this)}selectedOption={this.state.gaugeUnits}extractText={(option) => option}/><View style={styles.row}><Text style={styles.label}>Gauge:</Text><TextInputstyle={styles.input}onChangeText={(gauge) => this.setState({gauge: gauge})}value={this.state.gauge}placeholder={'5.5'}/></View></View><View style={styles.bordered}><Text style={styles.listing}>2. Type of Yarn</Text><PickerselectedValue={this.state.yarnType}onValueChange={(yarn) => this.setState({yarnType: yarn})}><Picker.Item label='Unknown' value='unknown' /><Picker.Item label='Sport' value='sport' /><Picker.Item label='Worsted' value='worsted' /><Picker.Item label='Bulky' value='bulky' /></Picker></View></View></ScrollView>);}
Notice the
When you browse around, you’ll find that there are a lot of plugins out there to extend React Native’s core library. The Android user interface benefits especially from component plugins. A great place to search for both React and React Native packages is https://js.coach/
3 - Navigation
On the other hand, Navigation is part of the React Native Core library. It comes in two flavors, Navigator and NavigatorIOS. The React Native team supports the use of Navigator, but most tutorials out there demonstrate NavigatorIOS due to its simplicity.
React Native supports platform-specific development through
The first two code snippets below are examples of iOS specific code using the
//index.ios.jsimport React, { AppRegistry, Component, NavigatorIOS } from "react-native";import Main from "./App/Components/main";import { styles } from "../styles";class KnitWhiz extends Component {render() {return (<NavigatorIOSstyle={styles.container}initialRoute={{title: "KnitWhiz",component: Main,}}/>);}}
// main.ios.js..handleSubmit(){this.props.navigator.push({title: 'Yardage',component: ShowYardage,passProps: this.calculateBySkein()}) //object literal}..render(){return(<View style={styles.container}><TouchableHighlightstyle={styles.submitButton}underlayColor="#5d4c8a"onPress={() => {this.handleSubmit()}}><Text style={styles.buttonText}>Calculate</Text></TouchableHighlight></View>);}
And these next two are code examples of navigation with
//index.android.jsimport React, {AppRegistry,Component,TouchableOpacity,Navigator,} from "react-native";import Main from "./App/Components/main";import Category from "./App/Components/category";import WrapsEstimator from "./App/Components/wraps_estimator";class KnitWhiz extends Component {constructor(props) {super(props);this.renderScene = this.renderScene.bind(this);}render() {return (<NavigatorinitialRoute={{ name: "Main", title: "KnitWhiz", passProps: {} }}renderScene={this.renderScene}configureScene={(route) => {if (route.sceneConfig) {return route.sceneConfig;}return Navigator.SceneConfigs.FloatFromRight;}}/>);}renderScene(route, navigator) {switch (route.name) {case "Main":return <Main navigator={navigator} {...route.passProps} />;case "Category":return <Category navigator={navigator} {...route.passProps} />;case "Wraps":return <WrapsEstimator navigator={navigator} {...route.passProps} />;default:return <Main navigator={navigator} {...route.passProps} />;}return (<View style={{ flex: 1, justifyContent: "center" }}><TouchableOpacitystyle={{ flex: 1, alignItems: "center", justifyContent: "center" }}onPress={() => navigator.pop()}><Text style={{ color: "purple", fontWeight: "bold" }}>Home</Text></TouchableOpacity></View>);}}
//main.android.jsvar React = require("react-native");import NavButton from "./nav_button";import Category from "./category";import WrapsEstimator from "./wraps_estimator";import SkeinEstimator from "./skein_estimator";import SwatchEstimator from "./swatch_estimator";import { styles } from "../styles";const { Navigator, TouchableOpacity, View, Text } = React;class Main extends React.Component {constructor(props) {super(props);this.renderScene = this.renderScene.bind(this);}handleNavigation(next_component, next_title) {this.props.navigator.push({title: "Sweater Info",name: "Category",passProps: {nextComponent: next_component,nextTitle: next_title,},});}render() {return (<NavigatorrenderScene={this.renderScene}navigator={this.props.navigator}navigationBar={<Navigator.NavigationBarstyle={{ backgroundColor: "#5d4c8a" }}routeMapper={NavigationBarRouteMapper}/>}/>);}renderScene(route, navigator) {return (<View style={styles.mainContainer}><Text style={styles.heading1}>How Much Yarn Do You Need?</Text><NavButtononPress={() => {this.handleNavigation("Wraps", "Wraps Info");}}text="Wraps"/><NavButtononPress={() => {this.handleNavigation("Skein", "Skein Info");}}text="Skein Info"/><NavButtononPress={() => {this.handleNavigation("Swatch", "Swatch Info");}}text="Swatch Info"/></View>);}}var NavigationBarRouteMapper = {LeftButton(route, navigator, index, navState) {return null;},RightButton(route, navigator, index, navState) {return null;},Title(route, navigator, index, navState) {return (<TouchableOpacity style={{ flex: 1, justifyContent: "center" }}><Textstyle={{color: "white",textAlign: "center",marginLeft: 40,fontSize: 20,}}>KnitWhiz Tools</Text></TouchableOpacity>);},};export default Main;
Highlighting the differences...
- NavigatorIOS
- Uses a ‘component’ property for identifying routes
- Automatically configures top-bar navigation using ‘title’ property
- Passes props from screen to screen with a ‘passProps’ property.
- Only available for iOS apps.
- Navigator
- May use any property for routing
- Separates navigation from rendering (with a specific sceneRender function)
- Allows for “Right” and “Left” buttons in top-bar navigation (Android)
- Passes props inside Navigator’s route property (which may be named anything -- here I’ve used “passProps” for consistency)
- May be used for both Android and iOS apps.
- Both
- Require an initialRoute prop
On a separate client project at MojoTech, we ended up using and contributing updates back to the react-native-simple-router project. This is a nice, small wrapper around the core React Native Navigator component and allowed us on that project to use a single Router Navigation system for both iOS and Android without diverging code.
4 - Styling
The next departure from React is the use of JavaScript rather than css for styling, through the style prop. React Native styling via the
Most tutorials tack on the styles used below the class definition, while I personally prefer to import them from a single external file. In ES6, for example:
// App/styles.js"use strict";const React = require("react-native");const { StyleSheet } = React;export const styles = StyleSheet.create({mainContainer: {flex: 1,marginTop: 55,flexDirection: "column",justifyContent: "center",backgroundColor: "#E8DDF4",},content: {flex: 1,padding: 40,},wrapContent: {flex: 1,padding: 40,marginTop: 50,},});
Styles may then be imported where needed:
import { styles } from "../styles";
Although the decision to go with a specific JavaScript “subset” of css styles caused me a bit of pain when I chose a style property which didn’t exist, e.g. a border property, the pain was ameliorated by a handy list of substitutes displayed within the error message, e.g. a borderWidth property.
And finally…
5 - Simulation & Deployment
The documentation does a good job of walking you through getting started with Android. I found it fairly straightforward, with one caveat: after configuring a physical device to use for testing, be sure to stop the Android AVD GUI as only one device may be used for testing. In other words, you won't be able to test simultaneously on an Android emulator and a physical device. Another alternative, Genymotion, is also a great simulator to use.
Once the app was tested on a device, actually deploying to Google Play required only following a few instructions and paying a few dollars. You can get started with Google play at https://support.google.com/googleplay/android-developer.
Apple requires a few more dollars and an iTunes Connect account. A good place to start is https://developer.apple.com/programs/.
Summing Up
Using React Native to get a mobile app up and selling was an efficient and thoroughly enjoyable experience for me. Here's wishing you all the best with your next app!
Cathy Zoller
P.S. We're ramping up our engineering team! Check out our open positions in Boulder and Providence.