September 02, 2015

From React to React Native in 30 Minutes

by José Magalhães

From React to React Native in 30 minutes

At Jscrambler, we’ve been doing React development for quite some time. So, we were excited to try out React Native and check if Facebook’s statement is true: ‘Learn Once, Write Anywhere’. Will I be able to reuse my knowledge with React and quickly be able to create a simple application in a short amount of time?

For the ones that never heard of React Native, it is a new library that allows you to use React on platforms such as mobile devices. The difference to React is that instead of HTML components, you use native components of the device where your application will run.

In this example, we’re building an application for iOS. You can find the source code in this GitHub repository.

Setup

We’ve started getting familiar with it by setting up a new project by following
the instructions found on the docs:

npm install - g react - native - cli
react - native init todo

The first command installs react-native-cli globally, giving us the react-native “binary” tool, while the second creates a project ready to work with Xcode (Mac OS X required) and another editor of choice (Atom).

The above commands will create the following directory structure:

iOS /
    node_modules /
    index.ios.js
package.json

Besides all the iOS files and assets, we can find an index.ios.js file inside the project which represents the entry point of our UI application. Inside there’s a typical React class (unfortunately at the time we’re writing this it’s still in ES5 flavor instead of an ES6 class).

Below the content of index.ios.js:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
'use strict';

var React = require('react-native');
var {
    AppRegistry,
    StyleSheet,
    Text,
    View,
} = React;

var todo = React.createClass({
    render: function() {
        return ( < View style = {
                styles.container
            } >
            < Text style = {
                styles.welcome
            } >
            Welcome to React Native!
            < /Text> < Text style = {
                styles.instructions
            } >
            To get started, edit index.ios.js < /Text> < Text style = {
                styles.instructions
            } >
            Press Cmd + R to reload, {
                '\n'
            }
            Cmd + D or shake
            for dev menu < /Text> < /View>
        );
    }
});

var styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },
    welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
    },
    instructions: {
        textAlign: 'center',
        color: '#333333',
        marginBottom: 5,
    },
});

AppRegistry.registerComponent('todo', () => todo);

At this point we can already see some differences: no HTML components at all, only components from the iOS SDK and a StyleSheet factory with style properties that resembles CSS but in fact are a bit limited to what iOS offers.

Protect your React App with Jscrambler

Development

Our goal is now to create a small interface with a list that allows us to add entries to it and persist them in the device so that it loads anytime we reopen the application.

Component State

In the component’s state, we’ll be storing the list of items of the to do list, that same list in the form of native DataSource and text field value of the input that allows creating new entries.

Keep in mind that this is only for the example sake; ideally, you would have different components to handle the list and the creation of a new entry.

getInitialState() {
    var itemsDS = new ListView.DataSource({
        rowHasChanged: (r1, r2) => r1 !== r2
    });
    var items = [];
    this.getStorage().then((items) => this.setItems(items));
    return {
        items,
        itemsDS: itemsDS.cloneWithRows(items),
            text: ''
    };
}

Rendering

First we’ve implemented our render method, a simple ScrollView composed by a ListView, a TextInput and a TouchableWithoutFeedback button:

render() {
        return ( < ScrollView >
            < ListView style = {
                styles.list
            }
            dataSource = {
                this.state.itemsDS
            }
            renderRow = {
                (rowData) => 
                   <Text>{rowData} </Text>
             }/>
             <TextInput style = {styles.input}
                onChange = {this.onChange}
                value = {this.state.text }/> 
             <TouchableWithoutFeedback onPress = {this.onPress} >
               <Text style = {styles.submit}> 
                  Add entry </Text> 
             < /TouchableWithoutFeedback> < /ScrollView>
            );
        }

Events

There are two main events in the UI: when we change the TextInput we must update the component state and when we press the TouchableWithoutFeedback component we must add an entry to our ListView data source.

onChange(event) {
    this.setState({
        text: event.nativeEvent.text
    });
}

onPress() {
    if (this.state.text) {
        this.state.items.push(this.state.text);
        this.setItems(this.state.items);
        this.setState({
            text: ''
        });
        this.updateStorage();
    }
}

setItems(items) {
    var itemsDS = this.state.itemsDS.cloneWithRows(items);
    this.setState({
        items, itemsDS
    });
}

Data Persistence

In order to persist the data of our application we’re using a native API called AsyncStorage, which is pretty similar to localStorage, the difference is that it’s async and it returns promises (yes!). We’ve created two methods to handle reading and writing to the storage:

getStorage() {
    return AsyncStorage
        .getItem('items')
        .then((items) => JSON.parse(items));
}

updateStorage() {
    return AsyncStorage.setItem('items', JSON.stringify(this.state.items));
}

Styling

Styling is not the focus of our experience, but you can easily tweak the look and feel of your application by using the StyleSheet API provided by React Native, in our example we’ve painted the Picasso with the following styles:

var styles = StyleSheet.create({
    list: {
        flex: 1,
        paddingBottom: 25,
        paddingRight: 25,
        paddingLeft: 25,
        marginBottom: 10,
        backgroundColor: '#F5FCFF'
    },
    input: {
        flex: 1,
        height: 25,
        marginRight: 20,
        marginLeft: 20,
        padding: 5,
        borderWidth: 1,
        borderColor: '#333',
        fontSize: 20
    },
    submit: {
        flex: 1,
        alignSelf: 'center',
        margin: 20,
        padding: 5,
        backgroundColor: '#333333',
        borderWidth: 1,
        borderColor: '#333',
        borderRadius: 20
    },
});

Objective-C Modules

In case you’re wondering how does React Native play with other Objective-C modules, it’s easy to export a module so that you can use it on your JavaScript code:

#
import "RCTBridgeModule.h"

@
interface MyModule: NSObject < RCTBridgeModule > @end

Then inside your JavaScript code:

var MyModule = require('NativeModules').MyModule;

Registering the component

Finally we must register our main component (ToDo) with the AppRegistry:

AppRegistry.registerComponent('todo', () => ToDo);

Conclusions

I’ve had next to zero experience with iOS development so I must say that it was nice to have a small application up and running in less than 30 minutes, though part of the documentation is incomplete (where’s the ListView.DataSource API described?) and React errors aren’t always helpful on finding the root cause.

Besides that, all the principles behind React still apply, so if you’re already experienced with it you’ll find yourself coding reusable components in no time.

Overall this looks like a very good alternative to other solutions such as Appcelerator and PhoneGap that unfortunately cut you down the direct access to the native APIs. In this case, you’re working native, with a little JavaScript sugar on top of it.

Learn once, write anywhere, but don’t forget to have the docs next to you.

Lastly, if you're building React applications with sensitive logic, be sure to protect them against code theft and reverse-engineering by following our guide.