React Native Tutorial: Building Mobile Apps with React Hooks

by Didin J. on Dec 17, 2019 React Native Tutorial: Building Mobile Apps with React Hooks

Tutorial as a point to start learning React Native from scratch by building Mobile Apps with React Hooks

In this React Native tutorial, we will build Mobile Apps with React Hooks. This tutorial can be a starting point to learn React Native from scratch with the new React Hooks. We are just not creating the legendary Hello World Mobile App. More than that, we will build simple Mobile apps that display a list of data, show data details and show Google maps. So, you will learn how to access local data, basic React Native routing, React Native basic components, and displaying Google Maps.

Table of Contents:

The following tools, frameworks, and modules are required for this tutorial:

  1. React Native
  2. Node.js (NPM or Yarn)
  3. React Native Elements
  4. React Native Maps
  5. Android Studio or SDK for Android
  6. XCode for iOS
  7. Terminal (OSX/Linux) or Command Line (Windows)
  8. Text Editor or IDE (We are using VSCode)

Before start to the main steps, make sure that you have installed Node.js and can run NPM or Yarn in the terminal or command line. To check the existing or installed Node.js environment open the terminal/command line then type this command.

node -v
v10.15.1
npm -v
6.13.1
yarn -v
1.19.2

You can watch the video version of this tutorial from our YouTube channel.


Install React Native CLI and Create App

For this React Native tutorial, we will use React Native CLI to create a React Native app. To install it, type this command in your App projects folder.

sudo npm install -g react-native-cli

Then create a React Native App using this command from your project directory.

react-native init ReactNativeTutorial

Next, go to the newly created React App folder and run the React Native app to the iOS simulator.

cd ReactNativeTutorial && npx react-native run-ios

Or run to the Android device/emulator.

cd ReactNativeTutorial && npx react-native run-android

When a new terminal window opened, go to the React Native project folder then run the Metro bundler server.

cd ~/Apps/ReactNativeTutorial && yarn start

Now, you will see this in the iOS simulator.

React Native Tutorial: Building Mobile Apps with React Hooks - Welcome


Add React Navigation Header and Screens

As we mention in the beginning, we will use a few screens for this React Native tutorial. For that, we need to add the components for each screen. Type these commands in the Terminal or Command line to add a folder and the components.

mkdir components
touch components/ListScreen.js
touch components/DetailsScreen.js
touch components/MapsScreen.js

That component files still the blanks Javascript file. We need to make it as the React Hooks main function files. Here's the basic ListScreen.js look like.

import React, { useState, useEffect } from 'react';
import { Button, View, Text } from 'react-native';

export default function ListScreen({ navigation }) {

    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>List</Text>
        </View>
    );
}

ListScreen.navigationOptions = ({ navigation }) => ({
    title: 'List',
});

Here's the DetailsScreen.js look like.

import React, { useState, useEffect } from 'react';
import { Button, View, Text } from 'react-native';

export default function DetailsScreen({ navigation }) {

    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>List</Text>
        </View>
    );
}

DetailsScreen.navigationOptions = ({ navigation }) => ({
    title: 'Details',
});

Here's the MapsScreen.js look like.

import React, { useState, useEffect } from 'react';
import { Button, View, Text } from 'react-native';

export default function MapsScreen({ navigation }) {

    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>List</Text>
        </View>
    );
}

MapsScreen.navigationOptions = ({ navigation }) => ({
    title: 'Maps',
});

Next, we will add the Navigation header in the screen layout for this Mobile Apps. For that, add the libraries of React Navigation and React Native Gesture Handler by type these commands.

yarn add react-navigation
yarn add react-native-gesture-handler

For iOS, go to the ios folder then install Pod.

cd ios
pod install

Next, we will implement stack navigation in the current App.js. Open and edit `App.js` then add these imports of react-navigation createAppContainer, react-navigation-stack createStackNavigator, ListScreen, DetailsScreen, and MapsScreen.

import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import ListScreen from './components/ListScreen';
import DetailsScreen from './components/DetailsScreen';
import MapsScreen from './components/MapsScreen';

Add a constant variable after the imports that initialize createStackNavigator.

const RootStack = createStackNavigator(
  {
    List: ListScreen,
    Details: DetailsScreen,
    Maps: MapsScreen,
  },
  {
    initialRouteName: 'List',
    defaultNavigationOptions: {
      headerStyle: {
        backgroundColor: '#19AC52',
      },
      headerTintColor: '#fff',
      headerTitleStyle: {
        fontWeight: 'bold',
      },
    },
  },
);

Add the above createStackNavigator to the createAppContainer.

const RootContainer = createAppContainer(RootStack);

Replace the current main function and the export with this export function.

export default function App() {
  return (
    <RootContainer />
  )
}


Display List of Data

We will use the array of objects data dan get from the Javascript file. For that, create a new Javascript file in the root of the project folder.

touch Team.js

Open and edit that file then add these lines of the array.

export const Team = [
  { id: 1, name: 'Arsenal',    image: require('./img/arsenal.png'), location: 'London (Holloway)', stadium:    'Emirates Stadium',
  capacity:    60704, manager: 'Unai Emery', captain: 'Pierre-Emerick Aubameyang', lat: 51.554929, lng: -0.108449 },
  { id: 2, name: 'Aston Villa',    image: require('./img/aston-villa.png'), location:    'Birmingham', stadium: 'Villa Park',
  capacity: 42785, manager: 'Dean Smith', captain: 'Jack Grealish', lat: 52.509131, lng: -1.884767 },
  { id: 3, name: 'Bournemouth',    image: require('./img/bornemouth.png'),    location:    'Bournemouth', stadium:    'Dean Court',
  capacity: 11364, manager: 'Eddie Howe', captain: 'Simon Francis', lat: 50.735278, lng: -1.838290 },
  { id: 4, name: 'Brighton & Hove Albion',    image: require('./img/brighton.png'), location: 'Brighton', stadium:    'Falmer Stadium',
  capacity: 30666, manager: 'Graham Potter', captain: 'Lewis Dunk', lat: 50.861606, lng: -0.083716 },
  { id: 5, name: 'Burnley',    image: require('./img/burnley.png'),    location:    'Burnley', stadium:    'Turf Moor',
  capacity: 21944, manager: 'Sean Dyche', captain: 'Ben Mee', lat: 53.789017, lng: -2.230187 },
  { id: 6, name: 'Chelsea',    image: require('./img/chelsea.png'),    location:    'London (Fulham)', stadium:    'Stamford Bridge',
  capacity: 41631, manager: 'Frank Lampard', captain: 'César Azpilicueta', lat: 51.481696, lng: -0.190957 },
  { id: 7, name: 'Crystal Palace',    image: require('./img/crystal-palace.png'), location: 'London (Selhurst)', stadium: 'Selhurst Park',
  capacity: 26047, manager: 'Roy Hodgson', captain: 'Luka Milivojević', lat: 51.398281, lng: -0.085489 },
  { id: 8, name: 'Everton',    image: require('./img/everton.png'),    location:    'Liverpool (Walton)', stadium: 'Goodison Park',
  capacity: 39221, manager: 'Marco Silva', captain: 'Séamus Coleman', lat: 53.438813, lng: -2.966331 },
  { id: 9, name: 'Leicester City',    image: require('./img/leicester.png'), location: 'Leicester', stadium: 'King Power Stadium',
  capacity:    32312, manager: 'Brendan Rodgers', captain: 'Wes Morgan', lat: 52.620399, lng: -1.142147 },
  { id: 10, name: 'Liverpool',    image: require('./img/liverpool.png'), location: 'Liverpool (Anfield)', stadium: 'Anfield',
  capacity: 54074, manager: 'Jürgen Klopp', captain: 'Jordan Henderson', lat: 53.430846, lng: -2.960844 },
  { id: 11, name:    'Manchester City',    image: require('./img/manchester-city.png'), location: 'Manchester',
  stadium: 'City of Manchester Stadium', capacity: 55017, manager: 'Pep Guardiola', captain: 'David Silva',
  lat: 53.483176, lng: -2.200427 },
  { id: 12, name:    'Manchester United',    image: require('./img/manchester-united.png'), location: 'Manchester', stadium: 'Old Trafford',
  capacity: 74879, manager: 'Ole Gunnar Solskjær', captain: 'Ashley Young', lat: 53.463077, lng: -2.291334 },
  { id: 13, name:    'Newcastle United',    image: require('./img/newcastle-united.png'), location: 'Newcastle', stadium: 'St James Park',
  capacity: 52354, manager: 'Steve Bruce', captain: 'Jamaal Lascelles', lat: 54.975581, lng: -1.621661 },
  { id: 14, name:    'Norwich City',    image: require('./img/norwich-city.png'), location: 'Norwich', stadium:    'Carrow Road',
  capacity: 27244, manager: 'Daniel Farke', captain: 'Grant Hanley', lat: 52.622219, lng: 1.309328 },
  { id: 15, name:    'Sheffield United',    image: require('./img/sheffield-united.png'), location: 'Sheffield', stadium: 'Bramall Lane',
  capacity: 32702, manager: 'Chris Wilder', captain: 'Billy Sharp', lat: 53.370374, lng:  -1.470880 },
  { id: 16, name:    'Southampton',    image: require('./img/southampton.png'), location: 'Southampton', stadium: 'St Marys Stadium',
  capacity: 32384, manager: 'Ralph Hasenhüttl', captain: 'Pierre-Emile Højbjerg', lat: 50.905860, lng: -1.390941 },
  { id: 17, name:    'Tottenham Hotspur',    image: require('./img/tottenham-hotspur.png'), location: 'London (Tottenham)',
  stadium:    'Tottenham Hotspur Stadium', capacity: 62214, manager: 'José Mourinho', captain: 'Hugo Lloris', lat: 51.604319, lng: -0.066381 },
  { id: 18, name:    'Watford',    image: require('./img/watford.png'), location: 'Watford', stadium: 'Vicarage Road',
  capacity: 20400, manager: 'Quique Sánchez Flores', captain: 'Troy Deeney', lat: 51.649959, lng: -0.401525 },
  { id: 19, name:    'West Ham United',    image: require('./img/westham-united.png'), location: 'London (Stratford)', stadium: 'London Stadium',
  capacity: 60000, manager: 'Manuel Pellegrini', captain: 'Mark Noble', lat: 51.538750, lng: -0.016625 },
  { id: 20, name:    'Wolverhampton Wanderers',    image: require('./img/wolverhampton.png'), location: 'Wolverhampton',
  stadium: 'Molineux Stadium', capacity: 32050, manager: 'Nuno Espírito Santo', captain: 'Conor Coady', lat: 52.590301, lng: -2.130418 }
];

Next, we will use a combination of React Native Elements ListItem and React to Native FlatList. For that, we need to install React Native Elements first and it's dependencies.

yarn add react-native-elements
yarn add react-native-vector-icons

For iOS, go to the ios folder then install Pod.

cd ios
pod install

Also, we need to add/replace this array of <key>UIAppFonts</key> in `ios/ReactNativeTutorial/Info.plist`.

<key>UIAppFonts</key>
<array>
    <string>AntDesign.ttf</string>
    <string>Entypo.ttf</string>
    <string>EvilIcons.ttf</string>
    <string>Feather.ttf</string>
    <string>FontAwesome.ttf</string>
    <string>FontAwesome5_Brands.ttf</string>
    <string>FontAwesome5_Regular.ttf</string>
    <string>FontAwesome5_Solid.ttf</string>
    <string>Foundation.ttf</string>
    <string>Ionicons.ttf</string>
    <string>MaterialIcons.ttf</string>
    <string>MaterialCommunityIcons.ttf</string>
    <string>SimpleLineIcons.ttf</string>
    <string>Octicons.ttf</string>
    <string>Zocial.ttf</string>
</array>

Otherwise, you will get one or more of these errors in the iOS simulator.

Unrecognized font family 'Ionicons'
Unrecognized font family 'Foundation'
Unrecognized font family 'MaterialIcons'
Unrecognized font family 'MaterialCommunityIcons'
Unrecognized font family 'SimpleLineIcons'
Unrecognized font family 'Zocial'
Unrecognized font family 'Feather'
...

Next, open and edit `components/ListScreen.js` then add these imports of react-native Stylesheet, FlatList, ActivityIndicator, View, Text, react-native-elements ListItem, Button, and Team data.

import { StyleSheet, FlatList, ActivityIndicator, View, Text } from 'react-native';
import { ListItem, Button } from 'react-native-elements';
import { Team } from '../Team';

Add these constant variables at the top of the main function body.

const [data, setData] = useState(Team);
const [showLoading, setShowLoading] = useState(true);

Add React Hooks useEffect to reload data.

useEffect(() => {
    setData(Team);
    setShowLoading(false);
}, []);

Add a condition to show loading spinner/text based on loaded data.

if(showLoading === true){
    return(
        <View style={styles.activity}>
            <ActivityIndicator size="large" color="#0000ff"/>
        </View>
    )
}

Add the constant variables for React Native FlatList keyExtractor and React Native Elements ListItem.

const keyExtractor = (item, index) => index.toString();

const renderItem = ({ item }) => (
    <ListItem
        title={item.name}
        leftAvatar={{ source: item.image }}
        onPress={() => {
            navigation.navigate('Details', {
                id: `${item.id}`,
            });
        }}
        chevron
        bottomDivider
    />
)

Replace the return function with this return function that contains React Native FlatList.

return (
    <FlatList
        keyExtractor={keyExtractor}
        data={data}
        renderItem={renderItem}
    />
);

Finally, add a required styles after the main function.

const styles = StyleSheet.create({
    container: {
        flex: 1,
        paddingBottom: 22
    },
    item: {
        padding: 10,
        fontSize: 18,
        height: 44,
    },
    activity: {
        position: 'absolute',
        left: 0,
        right: 0,
        top: 0,
        bottom: 0,
        alignItems: 'center',
        justifyContent: 'center'
    },
    message: {
        padding: 16,
        fontSize: 18,
        color: 'red'
    }
});


Show Data Details

We will show details of data using React Native Elements Card that contain Image, Button, Icon, View, and Text elements. Details data get from the filtered array of objects by ID. The ID gets from the React Native Navigation params. Open and edit `components/DetailsScreen.js` then add/modify these imports of react-native ScrollView, StyleSheet, Image, View, Text, react-native-elements Card, Button, Icon, and Team data.

import { ScrollView, StyleSheet, Image, View, Text } from 'react-native';
import { Card, Button, Icon } from 'react-native-elements';
import { Team } from '../Team';

Add a constant variable of details data inside the top of the main function body.

const [details, setDetails] = useState({
    id: null,
    name: '',
    image: '',
    location: '',
    stadium: '',
    capacity: null,
    manager: '',
    captain: '',
    lat: null,
    lng: null
});

Add the React Hooks useEffect after the constant variable.

useEffect(() => {
    const id = parseInt(navigation.getParam('id'), 0);
    setDetails(Team.find(x => x.id === id));
}, []);

Modify the return function to display the details of data using React Native elements.

return (
    <ScrollView>
        <Card style={styles.container}>
            <View style={styles.subContainer}>
            <View style={styles.detailsImage}>
                <Image
                style={{minWidth: 150, minHeight: 150}}
                source={details.image}
                />
            </View>
            <View style={styles.detailsItem}>
                <Text style={{fontSize: 16}}>Team Name: {details.name}</Text>
            </View>
            <View style={styles.detailsItem}>
                <Text style={{fontSize: 16}}>City: {details.location}</Text>
            </View>
            <View style={styles.detailsItem}>
                <Text style={{fontSize: 16}}>Stadium: {details.stadium}</Text>
            </View>
            <View style={styles.detailsItem}>
                <Text style={{fontSize: 16}}>Stadium Capacity: {details.capacity}</Text>
            </View>
            <View style={styles.detailsItem}>
                <Text style={{fontSize: 16}}>Manager: {details.manager}</Text>
            </View>
            <View style={styles.detailsItem}>
                <Text style={{fontSize: 16}}>Team Captain: {details.captain}</Text>
            </View>
            </View>
            <View style={styles.detailButton}>
                <Button
                    large
                    backgroundColor={'#19AC52'}
                    icon={
                        <Icon
                          name="map"
                          size={32}
                          color="white"
                        />
                    }
                    title='Show Maps'
                    onPress={() => {
                        navigation.navigate('Maps', {
                            lat: `${details.lat}`,
                            lng: `${details.lng}`
                        });
                    }} />
            </View>
        </Card>
    </ScrollView>
);


Display Google Maps

This step is a little bit complex because it involves the native iOS and Android configuration. We will use the react-native-maps module for this tutorial. For that, install that module first by type this command.

yarn add git+https://[email protected]/react-native-community/react-native-maps.git

We are adding a module using Git directly because the NPM module has an error for Android. Next, we will configure for each platform (iOS and Android). Before configuring this application natively, make sure you have enabled the Google Maps SDK for Android and iOS and have an API Key. We do not cover how to setup that in Google developer console this time, you can find in our other tutorial category.

iOS

After installing the module of react-native-maps, go to the iOS folder and install the Cocoapods.

cd ios
pod install

Next, open and edit `ios/ReactNativeTutorial/AppDelegate.m` then add this import.

#import <GoogleMaps/GoogleMaps.h>

Add this line to the top didFinishLaunchingWithOptions method body.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [GMSServices provideAPIKey:@"_YOUR_API_KEY_"];
  ...
}

You can fill "_YOUR_API_KEY_" with your Google Maps SDK for iOS API Key. Next, open and edit `ios/Podfile` then add these lines after other pods.

# React Native Maps dependencies
rn_maps_path = '../node_modules/react-native-maps'
pod 'react-native-google-maps', :path => rn_maps_path
pod 'GoogleMaps'
pod 'Google-Maps-iOS-Utils'

Type again this command to install the Cocoapods.

pod install

Android

After installing the react-native-maps module, open and edit `android/app/build.gradle` then add these lines to dependencies block.

dependencies {
    ...
    implementation(project(':react-native-maps')){
       exclude group: 'com.google.android.gms', module: 'play-services-base'
       exclude group: 'com.google.android.gms', module: 'play-services-maps'
    }
    implementation 'com.google.android.gms:play-services-base:10.0.1'
    implementation 'com.google.android.gms:play-services-maps:10.0.1'
    ...
}

Next, open and edit `android/app/src/main/AndroidManifest.xml` then add this line before the closing of </application>.

<application ...>
  ...
  <meta-data
      android:name="com.google.android.geo.API_KEY"
      android:value="_YOUR_API_KEY_"/>
</application>

Fill "_YOUR_API_KEY_" with your Google Maps SDK for Android API Key.

Next, we will implement Google Maps to the React Native functional component. Open and edit `components/MapsScreen.js` then add/modify these imports of react-native StyleSheet, View, and react-native-maps MapView.

import { StyleSheet, View } from 'react-native';
import MapView from 'react-native-maps';

Add the React Hooks useState constant variables to hold the latitude and longitude from the params.

const [latitude, setLatitude] = useState(parseFloat(navigation.getParam('lat')));
const [longitude, setLongitude] = useState(parseFloat(navigation.getParam('lng')));

Add this React Hooks useEffect to reload the longitude and latitude from the params.

useEffect(() => {
    setLatitude(parseFloat(navigation.getParam('lat')));
    setLongitude(parseFloat(navigation.getParam('lng')));
}, []);

Modify the return function to this return function that implements react-native-maps MapView.

return (
    <View style={styles.container}>
        <MapView
            initialRegion={{
            latitude: latitude,
            longitude: longitude,
            latitudeDelta: 0.0522,
            longitudeDelta: 0.0021,
            }}
            style={styles.map}
        />
    </View>
);

Don't forget to add the Stylesheet after the main function to show the Google Maps on the React Native screen.

const styles = StyleSheet.create({
    container: {
        flex: 1
    },
    map: {
        flex: 1
    }
})


Run and Test the React Native Mobile Apps

To run the React Native Mobile Apps on iOS simulator type this command.

react-native run-ios

After the new terminal tab opened, type this command immediately inside the same project folder.

yarn start

For Android, type this command.

react-native run-android

After the new terminal tab opened, type this command immediately inside the same project folder.

yarn start

And here the sample working application from this tutorial.

React Native Tutorial: Building Mobile Apps with React Hooks - List
React Native Tutorial: Building Mobile Apps with React Hooks - Details
React Native Tutorial: Building Mobile Apps with React Hooks - Maps

That it's, React Native Tutorial: Building Mobile Apps with React Hooks. You can find the full source code from our GitHub.

That just the basic. If you need more deep learning about React.js, React Native or related you can take the following cheap course:

Thanks!