11 min read

React Native was developed by Facebook, along with the lines of the React framework. Instead of rendering components to a browser’s DOM, React Native (RN) invokes native APIs to create internal components that are handled through your JS code. There are some differences between the usual HTML elements and RN’s components, but they are not too hard to overcome. With this tool, you are actually building a native app that looks and behaves exactly like any other native application, except that you use a single language, JS, for both Android and iOS development.

This article is taken from the book  Modern JavaScript Web Development Cookbook by Federico Kereki.  This book is a perfect blend of solutions for traditional JavaScript development and modern areas that developers have recently been exploring with JavaScript. This problem-solving guide teaches you popular problems solving techniques for JavaScript on servers, browsers, mobile phones, and desktops. To follow along with the examples implemented in this article, you can download the code from the book’s GitHub repository.

In this article, we’ll see how to install and use React Native to build a mobile application. We will also see how to add development tools like ESLint, Flow, and Prettier.

Setting up a RN application

There are three ways to set up a RN application: manually, which you won’t want to do; secondly, with packages, using the react-native-cli command-line interface; or lastly, by using a package very similar to create-react-native-app (or CRAN).

We start by getting a command-line utility, which will include plenty of other packages:

npm install create-react-native-app -g

Afterward, we can create and run a simple project with just three commands:

create-react-native-app yourprojectname
cd yourprojectname
npm start

How it works…

When you run your app, it starts a server at your machine, at port 19000 or 19001, to which you will connect using the Expo application. You can download Expo from its official website, which is available for both Android or iOS. Install it by following the instructions onscreen:

When you open the Expo app for the first time, it will look like the following screenshot:

Note that both the phone and your machine must be in the same local network, and your machine must also allow connections to ports 19000 and 19001; you may have to modify your firewall for this to work.

After you use the Scan QR Code option, there will be some synchronization, and soon you’ll get to see your basic code running with no problems:

Furthermore, if you modify the App.js source code, the changes will be immediately reflected in your device, which means all is well! To make sure this happens, shake the phone to enable the debugging menu, and make sure that Live Reload and Hot Reloading are enabled. You’ll also require Remote JS Debugging for later. Your phone should look as follows:

Adding development tools

Next, we need to add all the development tools required. We want to have ESLint for code checking, Prettier for formatting, and Flow for data types. CRAN takes care of including Babel and Jest, so we won’t have to do anything for those two.

How to do it…

As opposed React, where we need to add a special rewiring package in order to work with specific configurations, in RN, we can just add some packages and configuration files, and we’ll be ready to go.

Adding ESLint

For ESLint, we’ll have quite a list of packages we want:

npm install --save-dev \
 eslint eslint-config-recommended eslint-plugin-babel \
 eslint-plugin-flowtype eslint-plugin-react eslint-plugin-react-native

We’ll require a separate .eslintrc file, as in the case with React. The appropriate contents include the following:

{
    "parser": "babel-eslint",
    "parserOptions": {
        "ecmaVersion": 2017,
        "sourceType": "module",
        "ecmaFeatures": {
            "jsx": true
        }
    },
    "env": {
        "node": true,
        "browser": true,
        "es6": true,
        "jest": true,
        "react-native/react-native": true
    },
    "extends": [
        "eslint:recommended",
        "plugin:flowtype/recommended",
        "plugin:react/recommended",
        "plugin:react-native/all"
    ],
    "plugins": ["babel", "flowtype", "react", "react-native"],
    "rules": {
        "no-console": "off",
        "no-var": "error",
        "prefer-const": "error",
        "flowtype/no-types-missing-file-annotation": 0
    }
}

Adding Flow

Having completed that, ESLint is set to recognize our code, but we have to configure Flow as well:

npm install --save-dev flow flow-bin flow-coverage-report flow-typed

We’ll have to add a couple of lines to the scripts section of package.json:

"scripts": {
    "start": "react-native-scripts start",
    .
    .
    .
    "flow": "flow",
    "addTypes": "flow-typed install"
},

Then, we have to initialize the working directories of Flow:

npm run flow init

The contents of the .flowconfig file look like this:

[ignore]
.*/node_modules/.*
[include]

[libs]

[lints]
all=warn
untyped-type-import=off
unsafe-getters-setters=off

[options]
include_warnings=true

[strict]

Adding Prettier

There’s not much to installing Prettier, all we need is an npm command, plus the .prettierrc file. For the former, just use the following command:

npm install --save-dev prettier

For configuration, we can use the contents of this .prettierrc file:

{
    "tabWidth": 4,
    "printWidth": 75
}

How it works…

Let’s check that everything is OK. We’ll start by looking at the App.js file that was created by CRAN, and we can immediately verify that the tools work—because a problem is detected! Have a look at the following screenshot:

The rule that fails is a new one, from eslint-plugin-react-nativeno-color-literals, because we are using constants in styling, which could prove to be a maintenance headache in the future. We can solve that by adding a variable, and we’ll use a type declaration to make sure Flow is also running. The new code should be as follows:

// Source file: App.original.fixed.js
/* @flow */

import React from "react";
import { StyleSheet, Text, View } from "react-native";

export default class App extends React.Component<> {
render() {
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<Text>Changes you make will automatically reload.</Text>
<Text>Shake your phone to open the developer menu.</Text>
</View>
);
}
}

const white: string = "#fff";

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: white,
alignItems: "center",
justifyContent: "center"
}
});

Using native components

Working with RN is very much like working with React—there are components, state, props, life cycle events, and so on—but there is a key difference: your own components won’t be based on HTML but on specific RN ones. For instance, you won’t be using <div> elements, but rather <View> ones, which will be then mapped by RN to a UIView for iOS, or to an Android.View for Android.

Getting ready

We will start with an example of countries and regions page, which you can find in the book’s GitHub repository.  Since we are using PropTypes, we’ll need that package. Install it with the following command:

npm install prop-types --save

Then, we’ll have to install some packages, starting with Redux and relatives. Actually, CRAN already includes redux and react-redux, so we don’t need those, but redux-thunk isn’t included.  We can install it using the following command:

npm install react react-redux redux-thunk --save

We’ll also be using axios for async calls:

npm install axios --save

Our final step will be to run the server code (you can find it in the GitHub repo) so that our app will be able to do async calls. After downloading the server code from the GitHub repo, go to the directory, and just enter the following command:

node out/restful_server.js.

Let’s now see how we can modify our code to make it appropriate for RN.

How to do it…

Since RN uses its own components, your HTML experience will be of little use. Here, we’ll see some changes, but in order to derive the full benefits of all of RN’s possibilities, you’ll have to study its components on your own.

Let’s start with the <RegionsTable> component, which is rather simple:

// Source file: src/regionsApp/regionsTable.component.js
.
.
.

render() {
if (this.props.list.length === 0) {
return (
<View>
<Text>No regions.</Text>
</View>
);
} else {
const ordered = [...this.props.list].sort(
(a, b) => (a.regionName < b.regionName ? -1 : 1)
);

return (
<View>
{ordered.map(x => (
<View key={x.countryCode + "-" + x.regionCode}>
<Text>{x.regionName}</Text>
</View>
))}
</View>
);
}
}

Notice that there are no changes in the rest of the component, and all your React knowledge is still valid; you just have to adjust the output of your rendering method.

Next, we’ll change the <CountrySelect> component to use <Picker>, which is sort of similar, but we’ll require some extra modifications. Let’s take a look at our component, highlighting the parts where changes are needed:

// Source file: src/regionsApp/countrySelect.component.js
/* @flow */

import React from "react";
import PropTypes from "prop-types";
import { View, Text, Picker } from "react-native";

export class CountrySelect extends React.PureComponent<{
dispatch: ({}) => any
}> {
static propTypes = {
loading: PropTypes.bool.isRequired,
currentCountry: PropTypes.string.isRequired,
list: PropTypes.arrayOf(PropTypes.object).isRequired,
onSelect: PropTypes.func.isRequired,
getCountries: PropTypes.func.isRequired
};

componentDidMount() {
if (this.props.list.length === 0) {
this.props.getCountries();
}
}

onSelect = value => this.props.onSelect(value);

render() {
if (this.props.loading) {
return (
<View>
<Text>Loading countries...</Text>
</View>
);
} else {
const sortedCountries = [...this.props.list].sort(
(a, b) => (a.countryName < b.countryName ? -1 : 1)
);

return (
<View>
<Text>Country:</Text>
<Picker
onValueChange={this.onSelect}
prompt="Country"
selectedValue={this.props.currentCountry}
>
<Picker.Item
key={"00"}
label={"Select a country:"}
value={""}
/>
{sortedCountries.map(x => (
<Picker.Item
key={x.countryCode}
label={x.countryName}
value={x.countryCode}
/>
))}
</Picker>
</View>
);
}
}
}

Lots of changes! Let’s go through them in the order they occur:

  • An unexpected change: if you want a <Picker> component to display its current value, you must set its selectedValue property; otherwise, even if the user selects a country, the change won’t be seen onscreen. We’ll have to provide an extra prop, currentCountry, which we’ll get from the store, so we can use it as the selectedValue for our list.
  • The fired event when the user selects a value is also different; the event handler will be called directly with the chosen value, instead of with an event from which to work with event.target.value.
  • We have to replace the <select> element with <Picker>, and provide a prompt text prop that will be used when the expanded list is shown onscreen.
  • We have to use <Item> elements for the individual options, noting that the label to be displayed is now a prop.

Let’s not forget the change when connecting the list of countries to the store; we’ll only have to add an extra property to the getProps() function:

// Source file: src/regionsApp/countrySelect.connected.js
const getProps = state => ({
list: state.countries,
currentCountry: state.currentCountry,
loading: state.loadingCountries
});

Now, all we need to do is see how the main app is set up. Our App.js code will be quite simple:

// Source file: App.js
/* @flow */

import React from "react";
import { Provider } from "react-redux";

import { store } from "./src/regionsApp/store";
import { Main } from "./src/regionsApp/main";

export default class App extends React.PureComponent<> {
render() {
return (
<Provider store={store}>
<Main />
</Provider>
);
}
}

This is pretty straightforward. The rest of the setup will be in the main.js file, which has some interesting details:

// Source file: src/regionsApp/main.js
/* @flow */

import React from "react";
import { View, StatusBar } from "react-native";

import {
ConnectedCountrySelect,
ConnectedRegionsTable
} from ".";

export class Main extends React.PureComponent<> {
render() {
return (
<View>
<StatusBar hidden />
<ConnectedCountrySelect />
<ConnectedRegionsTable />
</View>
);
}
}

Apart from the usage of <View> wherever we would previously have used <div> (a change to which you should already have gotten used to), there’s an added detail: we don’t want the status bar to show, so we use the <StatusBar> element, and make sure to hide it.

How it works…

Just for variety, instead of using my mobile phone, as I did earlier in this article, I decided to use an emulated device. After starting the application with npm start, I started my device, and soon got the following:

If the user touches the <Picker> element, a popup will be displayed, listing the countries that were received from our Node server, as shown in the following screenshot:

When the user actually taps on a country, the onValueChange event is fired, and after calling the server, the list of regions is displayed, as follows:

Everything works, and is using native components; great! By the way, if you were not very sure about the selectedValue problem we described, just omit that prop, and when the user picks on a country, you’ll get a bad result:

This article walked you through the installation and set up the process of React Native and other development tools for developing the mobile version of a web app.

If you found this post useful, do check out the book, Modern JavaScript Web Development Cookbook.  You will learn how to create native mobile applications for Android and iOS with React Native, build client-side web applications using React and Redux, and much more.

Read Next

React Native 0.59 RC0 is now out with React Hooks, and more

The React Native team shares their open source roadmap, React Suite hits 3.4.0

How to create a desktop application with Electron [Tutorial]