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.
The following tools, frameworks, and modules are required for this tutorial:
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
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.
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 />
)
}
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 UIAppFonts 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'
}
});
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>
);
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.
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
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 ...>
...
<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
}
})
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.
That it’s, React Native Tutorial: Building Mobile Apps with React Hooks. You can find the full source code from our GitHub.
#React #ReactNative #MobileApp