Web Development

How to Set Up and Use Navigators in React Native

July 17th, 2020 | By Aman Mittal | 13 min read

React Navigation released its fifth stable version a few months ago. Even though the library is just a little over two years old, it is without a doubt one of the most popular navigation solutions in React Native apps and also has support for the Expo SDK.

The major highlight of this new release version is that the routing of screens is now based on component configuration.

In this tutorial, let's take a look at how to set up and use a stack navigation pattern between two screens, and pass data from one screen to another. The data is going to be fetched from a third-party API using GraphQL query language. This is possible using the Apollo client, which allows us to fetch results from a REST endpoint.

Prerequisites

To follow this tutorial, please make sure you have the following installed on your local development environment:

  • Node.js version >= 12.x.x installed

  • JavaScript/ES6 basics

  • Have access to one package manager, such as npm or yarn

  • the expo-cli version installed or use npx


The demonstrated example is based on Expo SDK 38.

Install dependencies

To start, generate a new Expo project with a blank template by running the following command in a terminal window:

npx expo init [Project Name]

# after the project directory has been generated

cd [Project Name]


Then, install the dependencies below for the react-navigation library to work. The first command is going to install the core packages of react-navigation. These core packages are used by navigators to create the navigation structure in the app.

The second command uses expo install instead of npm install or yarn add. The reason is that Expo is going to install the versions of the libraries mentioned that are compatible with the Expo SDK. This second command installs the peer dependencies that the react-navigation library depends on.

yarn add @react-navigation/native @react-navigation/stack

# use expo install for Expo projects only
expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view


Do note that the package @react-navigation/stack is only required to install when you are going to use the Stack navigation pattern in the app. For example, if you are just going to use tab navigation, you are going to install a different package.

What is a stack navigator?

The stack navigation pattern allows transitioning from one screen of the app to another while managing the navigation history. If the app uses only one stack navigator, then it is conceptually similar to how a web browser handles the navigation state.

The app pushes and pops screens from the navigation stack as users interact with it, and this results in the user seeing different screens.

Start by creating a new directory called src/navigation/, and inside it, create a new file called MainStackNavigator.js with the following import statements:

import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';


From the above snippet, the NavigationContainer is a component that manages the navigation tree. It contains the navigation state, and it has to wrap the structure of all navigators.

The createStackNavigator is a function that is used to implement a stack navigation pattern. This function returns two React components: Screen and Navigator, which help configure each component screen.

Since the app does not have any screen components for now, in the next section, let us create both the screens the app requires using some mock data to display.

Create app screens

Start by creating two screen component files called Home.js and Details.js inside the directory src/screens/ with the following code snippets:

// src/screens/Home.js
import React from 'react';
import { StyleSheet, View, Text } from 'react-native';

export default function Home() {
  return (
    <View style={styles.container}>
      <Text style={styles.text}>Home Screen</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#ebebeb'
  },
  text: {
    color: '#333',
    fontSize: 24,
    fontWeight: 'bold'
  }
});
// src/screens/Details.js
import React from 'react';
import { StyleSheet, View, Text } from 'react-native';

export default function Details() {
  return (
    <View style={styles.container}>
      <Text style={styles.text}>Details Screen</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#ebebeb'
  },
  text: {
    color: '#333',
    fontSize: 24,
    fontWeight: 'bold'
  }
});


The stack navigator needs screen components as routes. These routes are available in the form of screen components. Import these two screen components inside the MainStackNavigator.js file and then create the Stack Navigator function.

// other import statements

import Home from '../screens/Home';
import Details from '../screens/Details';

const Stack = createStackNavigator();

function MainStackNavigator() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={Home} />
        <Stack.Screen name="Details" component={Details} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

export default MainStackNavigator;


In the above snippet, there are two required props for each Stack.Screen. The prop name refers to the name of the route, and the prop component specifies which screen to render at the particular route. The order of the screens matters as the first screen defined in the stack navigator pattern is going to be the bottom of the stack.

Import this navigator inside the App.js component.

import React from 'react';
import MainStackNavigator from './src/navigation/MainStackNavigator';

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


To check if the configuration is working, start the development server with Expo start and then open the app either on a real device or in a simulator, as shown below.
React-Native-Navigators-Screen

Specifying options for each screen in Stack Navigator

By default, the title shown on each screen is the same as the value provided on the name attribute for each screen component in the stack navigator. However, you can set the title of the screen. Let us change the title of the screen shown, from “Home” to “Crypto List”.

This is done by specifying the options on each screen as shown below. Open the MainStackNavigator.js file and the prop options on Stack.Screen for the Home component.

<Stack.Screen name="Home" component={Home} options={{ title: 'Crypto List' }} />


The changes are instantly reflected in the Expo client.

Navigating between two screens

Open screens/Home.js and add a button component that is going to navigate from the Home screen component to the Details screen when pressed.

Import TouchableOpacity from react-native and make sure to use the navigation prop passed to the Home screen. This prop is passed to every screen that is a route wrapped by the current Stack Navigator in the app.

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

export default function Home({ navigation }) {
  return (
    <View style={styles.container}>
      <Text style={styles.text}>Home Screen</Text>
      <TouchableOpacity
        style={styles.buttonContainer}
        onPress={() => navigation.navigate('Details')}
      >
        <Text style={styles.buttonText}>Go to Detail Screen</Text>
      </TouchableOpacity>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#ebebeb'
  },
  text: {
    color: '#333',
    fontSize: 24,
    fontWeight: 'bold'
  },
  buttonContainer: {
    backgroundColor: '#222',
    borderRadius: 5,
    padding: 10,
    margin: 20
  },
  buttonText: {
    fontSize: 20,
    color: '#fff'
  }
});


Here is the output you are going to get after this step:
output-after-navigating-between-two-screens

When the user presses the button on the Home screen, the navigator will direct the user to the Details screen.

React Native Navigators Screen 4

Using screen options to modify the header

You can use the screenOptions prop to apply common styles to the header across the navigator. For example, in the code snippet below, let us set the properties, headerStyle, headerTintColor, and headerTitleStyle to change the background color of all screen headers as well as the color of the title on each screen.

  • headerStyle is a style object that can be used to set the background color of the header for the screen component.

  • headerTitleStyle is another style object that allows you to customize the title or the text of the header.

  • headerTintColor is the color property for both the back button and the title of the header.


Open the src/navigation/MainStackNavigator.js file to make these changes.

<Stack.Navigator
        screenOptions={{
          gestureEnabled: true,
          headerStyle: { backgroundColor: 'tomato' },
          headerTitleStyle: { fontWeight: 'bold' },
          headerTintColor: '#f8f8f8'
        }}
      >


With the basic configuration of a stack navigator working, let us now fetch the data from a third-party API using the Apollo client in the next section.

Install Apollo dependencies

Let's install all the required dependencies to integrate the Apollo client and request the REST endpoint using GraphQL queries:

yarn add apollo-client apollo-cache-inmemory graphql-tag apollo-link-rest apo


Configure the Apollo client in React Native

In this section, let us integrate the Apollo client so that we can fetch the data from the REST endpoint. Start by creating a new directory src/graphql and, inside it, also create a new file Client.js.

The Apollo-client package, along with Apollo-cache-inmemory and Apollo-link, is a fully-featured GraphQL client that can be integrated into React or React Native apps. Let us import all three of them into this file:

import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { RestLink } from 'apollo-link-rest';


For demo purposes, the API endpoint will be used as a REST endpoint from CryptoCompare.com. Make sure, at this point, that you have access to the API Key (which is free at the time of writing this tutorial).

Their API offers many endpoints for different use cases, but we are going to fetch several top coins by their total volume across all markets in the last 24 hours.

Add a RestLink for the REST API endpoint and pass headers, which is an object representing values to be sent as headers on the request:

const restLink = new RestLink({
  uri: 'https://min-api.cryptocompare.com',
  headers: {
    Authorization:
      'd251970548f7321b548d3fb61d58c1a456974ea02ba41437fc9bf711f4e89782'
  }
});


Add the following configuration with the default cache and RestLink to complete the configuration of the Apollo client:

export const client = new ApolloClient({
  link: restLink,
  cache: new InMemoryCache()
});


Now, open the App.js file to wrap the current stack navigator with ApolloProvider. This provider is similar to React's Context.Provider and places the Apollo client in the context. This makes them accessible to the Apollo client easily and from anywhere inside the component tree.

import React from 'react';
import MainStackNavigator from './src/navigation/MainStackNavigator';

import { ApolloProvider } from '@apollo/react-hooks';
import { client } from './src/graphql/Client';

export default function App() {
  return;
  <ApolloProvider client={client}>
    <MainStackNavigator />
  </ApolloProvider>;
}


Writing your first GraphQL query

In this section, let us write a query to hook the Apollo client to fetch results from the REST API endpoint. However, the query is going to be made in GraphQL query language with the help of graphql-tag.

In the src/graphql/ directory, create a new file called Queries.js and import graphql-tag. Then, create a query to fetch data. This query is called FETCH_COIN_LIST and is defined using a template from the gql tag. Using the @rest directive, Apollo manages the parsing of the query from a REST endpoint to the GraphQL API.

import gql from 'graphql-tag';

export const FETCH_COIN_LIST = gql`
  query FetchCoinsList {
    coinsList
      @rest(type: "ListPayload", path: "/data/top/totalvolfull?tsym=USD") {
      Data @type(name: "DataPayload") {
        CoinInfo @type(name: "CoinInfoPayload") {
          Id
          Name
          FullName
        }
        DISPLAY @type(name: "DisplayPayload") {
          USD @type(name: "USDPayLoad") {
            PRICE
            OPENDAY
            HIGHDAY
            LOWDAY
            OPEN24HOUR
          }
        }
      }
    }
  }
`;


Make a request to the REST endpoint with the Apollo client

Open the file screens/Home.js and import the FETCH_COIN_LIST query as well as the useQuery hook from @apollo/react-hooks.

The React hook useEffect is used below to test that the endpoint is fetching data as per our needs. The data fetched is going to be displayed in a console statement.

The hook useQuery is used to request the API endpoint by referencing the query FETCH_COIN_LIST. After being called, it returns a result object with a set of properties. We only need two properties for now: loading and data. De-structure this query hook inside the Home component as shown below.

import React, { useEffect } from 'react';
import { useQuery } from '@apollo/react-hooks';

import { StyleSheet, View, Text, TouchableOpacity } from 'react-native';
import { FETCH_COIN_LIST } from '../graphql/Queries';

export default function Home({ navigation }) {
  const { loading, data } = useQuery(FETCH_COIN_LIST);

  useEffect(() => {
    console.log(data);
  }, []);

  return (
    <View style={styles.container}>
      <Text style={styles.text}>Home Screen</Text>
    </View>
  );
}

// ... rest remains same


Sidenote: Don't forget that you should always protect your source code in commercial and enterprise apps to prevent tampering.

To see the result, make sure the expo start command is running in the terminal window. Then, go to the Expo client, either on a real device or a simulator, and open the developer menu on a Mac using:

  • if on the iOS simulator, press Ctrl-Cmd-Z

  • if on an Android emulator, press Cmd+M

  • if using a real device, just shake your device a bit


This is what the developer menu in an Expo client looks like:
developer-menu-appearance-in-expo-client

Choose the option Debug Remote JS. A debugger like below should appear in your default web browser.
debugger-example-from-choosing-the-option-debug-remote-js

Open the Console tab from the Developer Tools of the web browser. You are going to get the following result.
result-from-opening-the-console-tab-from-developer-tools-of-web-browser

That's it! The Apollo integration is working and you can start displaying the data in the app.

Add an Activity Indicator while fetching results

The useQuery hook gives one property called “loading” that can be used to indicate on the device's screen when the query is in the process of fetching the result. Using the ActivityIndicator, a loading indicator can be displayed.

Import the ActivityIndicator component from react-native in Home.js.

export default function Home({ navigation }) {
  const { loading, data } = useQuery(FETCH_COIN_LIST);

  if (loading && !data) {
    return (
      <View style={styles.loadingIndicatorContainer}>
        <ActivityIndicator size="large" color="#fff" />
      </View>
    );
  }
  return (
    <View style={styles.container}>
      <Text style={styles.boldText}>Coins List</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#333',
    justifyContent: 'center',
    alignItems: 'center'
  },
  boldText: {
    color: '#fff',
    fontSize: 24,
    fontWeight: 'bold'
  },
  loadingIndicatorContainer: {
    flex: 1,
    backgroundColor: '#333',
    justifyContent: 'center',
    alignItems: 'center'
  }
});



Display data in a list using FlatList

To display a list of items, let us create a separate component that can be reused for a different purpose if the scope of this app gets larger. Create a new directory called src/components, and inside it, place a new file called ListItem.js.

This component is going to display the name, full name, and price of the component, all inside a touchable button that is going to navigate to the Details screen you created earlier.

With some destructuring from the incoming props coin (which is going to be passed from Home.js), add a functional component called ListItem.

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

export default function ListItem(props) {
  const { coin, onPress } = props;
  const { CoinInfo, DISPLAY } = coin;
  const { FullName, Name } = CoinInfo;

  return (
    <TouchableOpacity
      style={styles.container}
      onPress={() => onPress && onPress(coin)}
    >
      <View style={styles.row}>
        <Text style={styles.text} numberOfLines={1}>
          {Name}
        </Text>
        <View style={styles.right}>
          <Text style={styles.text} numberOfLines={1}>
            {DISPLAY.USD.PRICE}
          </Text>
        </View>
      </View>

      <View style={styles.row}>
        <Text style={[styles.text, styles.name]} numberOfLines={1}>
          {FullName}
        </Text>
      </View>
    </TouchableOpacity>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20
  },
  active: {
    backgroundColor: 'rgba(255,255,255,0.05)'
  },
  row: {
    flexDirection: 'row',
    justifyContent: 'space-between'
  },
  right: {
    flex: 1,
    alignSelf: 'flex-end',
    alignItems: 'flex-end'
  },
  text: {
    color: '#FFFFFF',
    fontSize: 24,
    fontWeight: '500'
  },
  name: {
    color: 'rgba(255,255,255,0.5)',
    fontSize: 16,
    fontWeight: '300'
  }
});


Now, import this component in the Home.js file. Also, import FlatList from the react-native core.

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

import ListItem from '../components/ListItem';
//...


Next, add this FlatList component wrapped inside the root View component, like below.

<View style={styles.container}>
  <FlatList
    contentContainerStyle={styles.contentContainerStyle}
    data={data.coinsList.Data}
    keyExtractor={item => item.CoinInfo.Id.toString()}
    renderItem={({ item }) => {
      return (
        <ListItem
          coin={item}
          onPress={() => navigation.navigate('Details', { coin: item })}
        />
      );
    }}
  />
</View>


Here is the output after this step:
output-of-adding-FltList-component-wrapped-inside-the-root-View-component

You are also going to see the initial loading screen.

React Native Navigators Screen 11

Since all the props are being passed from the Home screen to the Details screen and the navigation pattern is working, let us set up the Details screen now. Once route.params is de-structured into an object, you can use the values from that object to get the data passed from the Home screen.

export default function Details(props) {
  const { route } = props;
  const { params } = route;
  const { coin } = params;
  const { CoinInfo, DISPLAY } = coin;
  const { FullName, Name } = CoinInfo;
  const { USD } = DISPLAY;
  const { PRICE, OPENDAY, HIGHDAY, LOWDAY, OPEN24HOUR } = USD;

  return (
    <View style={styles.container}>
      <View style={styles.header}>
        <Text numberOfLines={1} style={styles.text}>
          {Name} - {FullName}
        </Text>
        <Text style={styles.priceText} numberOfLines={1}>
          Price: {PRICE}
        </Text>
      </View>
      <View style={styles.statsContainer}>
        <View>
          <View style={styles.statRow}>
            <Text style={styles.stat} numberOfLines={1}>
              Open Day
            </Text>
            <Text style={styles.stat} numberOfLines={1}>
              {OPENDAY}
            </Text>
          </View>
          <View style={styles.statRow}>
            <Text style={styles.stat} numberOfLines={1}>
              Highest in a day
            </Text>
            <Text style={styles.stat} numberOfLines={1}>
              {HIGHDAY}
            </Text>
          </View>
          <View style={styles.statRow}>
            <Text style={styles.stat} numberOfLines={1}>
              Lowest in a day
            </Text>
            <Text style={styles.stat} numberOfLines={1}>
              {LOWDAY}
            </Text>
          </View>
          <View style={styles.statRow}>
            <Text style={styles.stat} numberOfLines={1}>
              Open in 24 hours
            </Text>
            <Text style={styles.stat} numberOfLines={1}>
              {OPEN24HOUR}
            </Text>
          </View>
        </View>
      </View>
    </View>
  );
}


Also, add the corresponding styles to this component:

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff'
  },
  header: {
    flex: 30,
    justifyContent: 'center',
    alignItems: 'center'
  },
  text: {
    fontSize: 32,
    color: '#161616'
  },
  priceText: {
    fontSize: 24,
    color: '#161616'
  },
  statsContainer: {
    flex: 62,
    backgroundColor: '#161616'
  },
  statRow: {
    padding: 10,
    flexDirection: 'row',
    justifyContent: 'space-between'
  },
  stat: {
    color: '#fff',
    fontSize: 16,
    fontWeight: '500'
  }
});


On visiting the details of any coin in the list, the following is going to be displayed.
details-of-visiting-any-coin-in-the-list

Conclusion

In this tutorial, we have discussed many strategies and properties that you can apply and implement in your Stack navigator.

The first objective is to get familiar with the component-based configuration of the Stack Navigator in the latest version of the react-navigation library.

The second objective is also fulfilled, which is to use the REST endpoint and integrate the Apollo client into any API endpoint to query desired results in a React Native or Expo app and pass the data between two screens.

Jscrambler

The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.

View All Articles

Must read next

Web Development

Build a Chat App with Firebase and React Native

In this tutorial, let's build a mobile chat application using React Native, Expo and Firebase - a great way to evolve your JavaScript skills.

November 26, 2021 | By Aman Mittal | 13 min read

Application Security

Securing React Native Applications

React Native is the framework of choice for cross-platform mobile development. Here, we explore several strategies to secure React Native applications.

August 12, 2022 | By Jscrambler | 18 min read

Section Divider