A simple implementation of Context API in React Native

This tutorial will help you understand the concept of Context API in the simplest way possible.

Chaudhry Talha 🇵🇸
9 min readApr 30, 2021

What we’ll do:

We’ll create two tabs in the tab bar and each tab will have four screens in a Navigation Stack.

In three of those screens, we’ll have TextInput where the user will enter a value, and on the 4th screen, we’ll access those three values of text fields using Context API.

The reason for having two tabs is to show you that you can have multiple contexts in your app and each can have its own set of screens. You need to structure your app as neatly as possible and for a quick brief of this statement have a look at the image below:

Figure 1: App Tree Structure

The above image is exactly how we’ll structure our app. You can see the app structure is pretty straight forward and we have two contexts i.e. Context 1 and Context 2. Context 1 will hold values in StacNavA and will have no idea about StackNavB values and vice versa. This means Screens from 1 to 4 will be able to use any value within Context 1 and Screens in StackNavB will only be able to access the values we put inside Context 2. You can have a third context for the Tab bar which will cover all the Stack Navs as well, which would be useful if let’s say you have authentication in your app and want to use the Email or name of the user anywhere in the app.

Don’t worry if you don’t get the concept fully, the implementation will make it more clear.

There are two things to remember about context API:

  1. Provider: To make the context available to all the react-native components, we have to use a Provider. In figure 1 above Context 1 and, Context 2 are our providers.
  2. Consumer: When you need to read a value stored using a context. In the case of figure 1, Screens 1 to 8 all will be consumers but they will only be able to consume values within their respective context.

Let’s Begin

Start by creating a new react-native project

react-native init ContextAPITutorial

As we’ll be navigating between screens so let’s install react-navigation packages:

npm i @react-navigation/native
npm i react-native-reanimated
npm i react-native-gesture-handler
npm i react-native-screens
npm i react-native-safe-area-context
npm i @react-native-community/masked-view
npm i @react-navigation/bottom-tabs
npm i @react-navigation/stack

If you’re going to demonstrate on iOS the next step is to run npx pod-install after installing the above libraries.

Create a folder in your project named src and inside that folder create another folder named Screens and inside this folder create 8 .js files named Screen1.js, Screen2.jsScreen8.js.

You can go ahead and add some basic boilerplate to each of these files but I’ll go ahead and set the App.js first and then I’ll come back to the screens.

We’ll first set App.js in which we create the exact structure as given in figure 1, I’ll even try to keep the naming the same as figure 1. But for reference let's take this example that StackNavA will have information of the passenger traveling from one country to another and StackNavB will contain information of the ticket like PNR etc.

Remove everything from App.js and start by importing the following:

import React, { useState } from 'react';import { NavigationContainer } from '@react-navigation/native';import { createStackNavigator } from '@react-navigation/stack';import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';import Screen1 from './src/Screens/Screen1';import Screen2 from './src/Screens/Screen2';import Screen3 from './src/Screens/Screen3';import Screen4 from './src/Screens/Screen4';import Screen5 from './src/Screens/Screen5';import Screen6 from './src/Screens/Screen6';import Screen7 from './src/Screens/Screen7';import Screen8 from './src/Screens/Screen8';

Pretty straightforward thing as we import the 8 screens and navigation-related packages, we’re also imported useState hook which we’ll use shortly. (Note: If you don’t have any code inside screens there might be errors, so for now just follow along and everything will be okay by the end)

Next, we’ll initialize the Tab and Stack.

const Tab = createBottomTabNavigator();const Stack = createStackNavigator();

I’ll go ahead and create two contexts as well. Because based on the example and figure 1 we’re taking we need two contexts in the app. So, after the above two const we’ll create two new const and export them at the same time:

export const Context1 = React.createContext(null);export const Context2 = React.createContext(null);

Next, I’ll create a function and name it StackNavA in which I’ll handle everything there is to this side of our tree:

function StackNavA() {
//Context 1const context1InitialState = {fullName: null,destinationCountry: null,departureCountry: null};const [passengerInfo, setPassengerInfo] = useState(context1InitialState);function setFullName(fullName) {const newState = { ...passengerInfo, fullName };setPassengerInfo(newState);}function setDestinationCountry(destinationCountry) {const newState = { ...passengerInfo, destinationCountry };setPassengerInfo(newState);}function setDepartureCountry(departureCountry) {const newState = { ...passengerInfo, departureCountry };setPassengerInfo(newState);}const context1Setters = {setFullName,setDestinationCountry,setDepartureCountry}return (<Context1.Provider value={{ ...passengerInfo, ...context1Setters }}><Stack.Navigator><Stack.Screen name="Screen 1" component={Screen1} /><Stack.Screen name="Screen 2" component={Screen2} /><Stack.Screen name="Screen 3" component={Screen3} /><Stack.Screen name="Screen 4" component={Screen4} /></Stack.Navigator></Context1.Provider>)}

At first, we have created a variable that holds the initial state which is nothing more than an object that tells us how the structure of the data is going to be.

After that, we have created a state which we’ll use to update the information in the initial state variable and that is why we have initialized it with context1InitialState. I’ve named it passengerInfo because of the example we’re taking for this tutorial.

Next, I’ve three-setters functions all setting the respective values, and then at the end, I’ve just combined the three setters into one object i.e. context1Setters

Finally, in the return statement, I’ve wrapped the four screens Screen1…4 into a Provider Context1.Provider and in values, I’ve provided it with the state value and the setters functions.

You’ll see all of this coming together as you move along.

Next, we’ll do the exact same things for our context 2:

function StackNavB() {//Context 2const context2InitialState = {pnrNumber: null,totalBagsChecked: null,comments: null};const [passengerCheckInInfo, setPassengerCheckInInfo] = useState(context2InitialState);function setPNRNumber(pnrNumber) {const newState = { ...passengerCheckInInfo, pnrNumber };setPassengerCheckInInfo(newState);}function setTotalBagsChecked(totalBagsChecked) {const newState = { ...passengerCheckInInfo, totalBagsChecked };setPassengerCheckInInfo(newState);}function setComments(comments) {const newState = { ...passengerCheckInInfo, comments };setPassengerCheckInInfo(newState);}const context2Setters = {setPNRNumber,setTotalBagsChecked,setComments}return (<Context2.Provider value={{ ...passengerCheckInInfo, ...context2Setters }}><Stack.Navigator><Stack.Screen name="Screen 5" component={Screen5} /><Stack.Screen name="Screen 6" component={Screen6} /><Stack.Screen name="Screen 7" component={Screen7} /><Stack.Screen name="Screen 8" component={Screen8} /></Stack.Navigator></Context2.Provider>)
}

Each step in StackNavB function is the same as StackNavA and as you can see it contains screens 5 to 8 and its own context i.e. Context2

Finally, we’ll add the tab bar to our App.js

function App(props) {return (<NavigationContainer><Tab.Navigator><Tab.Screen name="StackNav A" component={StackNavA} /><Tab.Screen name="StackNav B" component={StackNavB} /></Tab.Navigator></NavigationContainer>);}export default App;

That’s it for App.js.

Now we’ll set up the Screens functionalities. Screens 1 to 3 will have the same functionalities so I’ll only explain the Screen 1 code and then Screen 4.

import React, { useContext } from 'react';import { Button, Text, TextInput, View } from 'react-native';import { Context1 } from '../../App';function Screen1({ navigation }) {const context = useContext(Context1)return (<View><Text>Enter your full name</Text><TextInputplaceholder={'Full Name'} value={context.fullName}onChangeText={(name) => {context.setFullName(name)}} /><Button title={'NEXT'} onPress={() => {navigation.navigate('Screen 2')}} /></View>);}export default Screen1;

We first imported our Context1 and then created a variable where we used the useContext hook to initialize the variable with Context 1. The in the TextInput I’ve provided the value field to auto-populate if there is some value stored in the context and when the user will type I’m calling the setter method of full name and that’s pretty much it. Now I’ll add functionality that is exactly the same concept as Screen1 to Screen 2 and 3.

import React, { useContext } from 'react';import { Button, Text, TextInput, View } from 'react-native';import { Context1 } from '../../App';function Screen2({ navigation }) {
const context = useContext(Context1)return (<View><Text>Enter your destination country</Text><TextInputplaceholder={'Destination Country'} value={context.destinationCountry}onChangeText={(dest) => {context.setDestinationCountry(dest)}} /><Button title={'NEXT'} onPress={() => {navigation.navigate('Screen 3')}} /></View>);}export default Screen2;

and Screen 3

import React, { useContext } from 'react';import { Button, Text, TextInput, View } from 'react-native';import { Context1 } from '../../App';function Screen3({ navigation }) {
const context = useContext(Context1)
return (<View><Text>Enter your departure country</Text><TextInputplaceholder={'Departure Country'} value={context.departureCountry}onChangeText={(depart) => {context.setDepartureCountry(depart)}} /><Button title={'NEXT'} onPress={() => { navigation.navigate('Screen 4') }} /></View>);}export default Screen3;

Now for Screen 4, we’ll be displaying the info so I’ve just added a few more components:

import React, { useContext } from 'react';import { Button, Text, TextInput, View } from 'react-native';import { Context1 } from '../../App';function Screen4({ navigation }) {
const context = useContext(Context1)
const {fullName,destinationCountry,departureCountry,} = contextreturn (<View><Text>Departure country</Text><Text>{departureCountry}</Text><Text>Destination country</Text><Text>{destinationCountry}</Text><Text>Passenger Name</Text><Text>{fullName}</Text><Text>Is the above info correct?</Text><Button title={'YES'} onPress={() => {alert("Yay everything worked well!")navigation.popToTop()}} /><Button title={'NO'} onPress={() => {alert("It's okay you can enter the information again")navigation.popToTop()}} /></View>);}export default Screen4;

I’ve broken the object context into the same structure so that I can easily use it. Otherwise, I could’ve also used context.fullName. So now, I can import this context anywhere and use it.

If you run the app now you’ll see it works like you have wanted it to work.

We’re successfully retrieving values via Context API. Now, I’ll leave you here to do the same thing for Screen 5 till 8 in StackNavB.

So you just saw how we can use Context API to set and retrieve data without props.

The complete source code of this tutorial is available here:

Feel free to add a comment or open an issue on GitHub if you face any issue regarding this I’ll be happy to help.

Possible Errors/Warnings:

If you face REQUIRE CYCLE warning have a look at this answer on StackOverflow:

https://stackoverflow.com/questions/60489403/react-native-with-context-api-warnings-require-cycles-are-allowed-but-can-res

As always if you find this helpful share and press the 👏🏻 button so that others can find it too. If you see a typo feel free to highlight it or if you’re stuck drop a comment and I’ll try my best to help you.

All my tutorials are free but if you feel like supporting you can buymeacoffee.com/chaudhrytalha

Happy Coding 👨🏻‍💻

--

--