In a typical mobile application, there are multiple instances that a mobile device can lose internet connectivity. This can result from myriads of reasons, from internet subscription exhaustion to sudden drop in internet speed to users turning off their internet connectivity altogether.
In these instances, how our React native application reacts to the disconnected internet is very important. If adequate strategies are not in place, the network request can keep the spinner spinning even though the connection is lost, or worst still, the application can react to the no data state and make an unnecessary update to our application. Comes in React native net-info library. Link
In this tutorial, we'll cover in detail
- What does the Netinfo library do?
- Listening for network change
- Toggling component based on internet connectivity
- An example, building your Internet detection component
First things first, let's install the libraries we'll be using for this tutorial,
npm @react-navigation/native @react-navigation/native-stack react-native-screens react-native-safe-area-context install
We need these libraries to create and manage the app's navigationRead more
npm install axios
For making our network request, we'll use the library axios
and the NetInfo library.
npm install @react-native-community/netinfo
So, let's get to it
What the Netinfo library is really
The Netinfo is an API formally maintained and managed by the react native team. The team however stopped maintaining the API and advised the community package be used instead. What the API does is expose some key properties, methods, and event listeners that we can watch for in our application to respond appropriately. some of the exposed functionalities include.
The useNetInfo hook
The hook exposes details about the device's internet connectivity;
{
"details": {
"bssid": "02:00:00:00:00:00",
"frequency": 2447,
"ipAddress": "10.0.2.16",
"isConnectionExpensive": false,
"linkSpeed": 19,
"rxLinkSpeed": 19,
"strength": 99,
"subnet": "255.255.255.255",
"txLinkSpeed": 19
},
"isConnected": true,
"isInternetReachable": true,
"isWifiEnabled": true,
"type": "wifi"
}
We can access properties like the isConnected
to check whether or not a device is connected to the internet before making a network request.
The addEventListener Method
This is a very important part of the NetInfo library. It gives us the flexibility to watch for the network state change in devices and react accordingly. It works typically by passing a callback function, whose parameter serves as the current state of the internet connectivity. As an example:
const Example = () => {
useEffect(() => {
const unsubscribe = NetInfo.addEventListener(currentState => {
console.log(`Device is ${currentState.isConnected ? 'Connected': 'not connected'}`);
});
return () => unsubscribe();
}, [])
return (
<Text>
Working With NetInfo
</Text>
)
}
What did we just do?
- We need to first import our
useEffect
hoot from react, we need this because, we have to leverage theuseEffect
clean-up function, which works typically like thecomponentDidUnmount
, calling our unsubscribe function in the cleanup function. - We also imported the NetInfo library and tie a callback function to the addEventListener method, passing the currentState argument, representing the current state of the internet connectivity.
There are still quite a few methods returned from the NetInfo
library but for this tutorial, we'll limit it to those mentioned.
Listening for network change
We can explore a few options to listen for the change in internet connectivity. We can decide to set up redux to update a global state value when the network state changes, to be sure that all components get access to the new state. Another option is to leverage react context API
, to perform a similar action in setting and updating the network context based on the current network state.
So, let's get right to it.
Setting up context
The context API is a very powerful bit in react, giving us the flexibility to create a global state in our application with very minimal code. Read More about react context
Firstly, let's create a file and call it NetworkContext.js
import React, { createContext, useState } from 'react'
export const NetworkContext = createContext({
isConnected: false,
setIsConnected: () => null,
})
export const NetworkProvider =({ children }) => {
const [isConnected, setIsConnected] = useState(false)
const value = { isConnected, setIsConnected }
return <NetworkContext.Provider value={value}>{children}</NetworkContext.Provider>
}
What did we just do?
- We imported the createContext from react, and exported the NetworkContext. What this does is use the
useState
hook to create a global state, and exported the NetworkProvider, passing theisConnected
and thesetConnected
as values of the NetworkContext Provider.
Now, need to create the component that will serve as the ParentComponent, which will be monitoring the network change. This component will be wrapped around all the pages of the app. The idea is that by having the ParentComponent wrapping all other components, we can from this component manage the network state. That way, on all pages of the app, we can display a notification whenever the app loses internet connectivity. In this component, we need to import our NetworkContext to update the context from here and access it in all nested components.
Let's put this into writing.
import React, { useState, useContext, useEffect } from "react";
import { Text, View} from "react-native";
import NetInfo from "@react-native-community/netinfo";
import { NetworkContext } from "./NetworkContext"; //importing the NetworkContext
const ParentComponent = ({ children }) => {
const [isOffline, setOfflineStatus] = useState(false);
const { isConnected, setIsConnected } = useContext(NetworkContext) //using the createContext to access the setIsConnected state
useEffect(() => {
const removeNetInfoSubscription = NetInfo.addEventListener((state) => {
const offline = !(state.isConnected && state.isInternetReachable);
setOfflineStatus(offline);
setIsConnected(offline)
});
return () => removeNetInfoSubscription();
}, []);
return (
<View style={{
flex: 1
}}>
{children} //The nested component
</View>
)
}
Toggling component based on internet connectivity
The next thing is to create a component that will be displayed based on whether or not the device is connected to the internet.
create a file, name it NoInternet.js
import React from "react";
import { Animated, StatusBar,StyleSheet, Text } from "react-native";
const styles = StyleSheet.create({
container: {
width: '100%',
height: 40,
backgroundColor: 'red',
padding: 5,
paddingLeft: 10,
position: 'absolute',
top: 0,
zIndex: 100
},
text: {
fontSize: 17,
color: '#fff'
}
})
const NoInternet = () => {
return (
<Animated.View style={[styles.container]}>
<StatusBar
backgroundColor='red'
/>
<Text style={styles.text}>
No Internet Connection
</Text>
</Animated.View>
)
}
export default NoInternet
The heavy lifting has been done already, so let's create our pages and also set up our app's navigation just so we can move between pages and be sure that our NoInternet component will be displayed on every component when we go offline.
Firstly, let's create a screen, name it Home.js
import React from "react";
import { Text, View, TouchableOpacity } from "react-native";
import ParentComponent from "./Network";
const HomePage = ({ navigation }) => {
return (
<ParentComponent>
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}}
>
<Text>The Home Page</Text>
<TouchableOpacity
onPress={() => navigation.navigate('Settings')}
style={{
width: '90%',
height: 50,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'teal'
}}
>
<Text style={{
color: 'white'
}}>
Go to Settings Page
</Text>
</TouchableOpacity>
</View>
</ParentComponent>
)
}
export default HomePage
What just happened?
- We imported our
ParentComponent
, Wrapping ourHomePage
component as its child, that way, we can display the NoInternet component when the device is offline - We also need to destructure our navigation prop to be able to move between screens.
Next up, let's create another page and name it Settings.js
import React from "react";
import { Text, View, TouchableOpacity } from "react-native";
const SettingPage = ({ navigation }) => {
return (
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}}
>
<Text>The Setting Page</Text>
<TouchableOpacity
onPress={() => navigation.navigate('Home')}
style={{
width: '90%',
height: 50,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'teal'
}}
>
<Text style={{
color: 'white'
}}>
Go to Home Page
</Text>
</TouchableOpacity>
</View>
)
}
export default SettingPage
The same thing applies, we wrapped our ParentComponent
over our Settings.js
page to display the NoInternet component and also destructured the navigation prop to move between screens.
Lastly, we need to set up the navigation in our App.js to handle moving between screens. You can read more about moving between screens here
import React from 'react';
import {
StyleSheet,
} from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import HomePage from './src/Home';
import SettingPage from './src/Setting';
const Stack = createNativeStackNavigator()
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: false
}}
>
<Stack.Screen name='Home' component={HomePage} />
<Stack.Screen name='Settings' component={SettingPage} />
</Stack.Navigator>
</NavigationContainer>
)
};
export default App;
We need to make some adjustments to our parent component, to toggle the NoInternet.js
component based on the internet connectivity state.
import NoInternet from "./NoInternet"; //import the NoInternet Component
Add the line of code to the returned statement in the ParentComponent
{isOffline && <NoInternet />}
- What was that? We imported the NoInternet component and based on the
isOffline
state in theParentComponent
, we display theNoInternet.js
component Great, we can not test our implementation
Everything seems to be working fine. Now, let's try making use of the isConnected
we get from the NetworkContext
, to prevent making an API call when we are not connected to the internet.
Firstly, let's create a NetworkModal component that will be displayed whether or not the device is connected with a network request is about to be made.
import React, { useState } from 'react'
import { Text, TouchableOpacity, Modal, StyleSheet, View} from 'react-native';
import { useNetInfo } from '@react-native-community/netinfo';
const NetworkModal = ({visible, setVisible}) => {
const netinfo = useNetInfo();
return (
<Modal
visible={visible}
onRequestClose={() => setVisible(!visible)}
animationType='fade'
transparent={true}
>
<View
style={styles.container}
>
<View
style={styles.box_1}
>
<Text style={styles.header}>
Oops
</Text>
<Text
style={styles.text}
>
Seems like you are not connected to the internet,
Please check your connectivity and try again
</Text>
<TouchableOpacity
onPress={() => {
if(netinfo.isConnected){
setVisible(false)
}
}}
style={styles.box_2}
>
<Text style={styles.try_again}>
Try Again
</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'rgba(0,0,0,0.5)',
justifyContent: 'center',
alignItems: 'center'
},
box_1: {
width: '80%',
height: 250,
borderRadius: 10,
backgroundColor: 'white',
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
},
header: {
fontSize: 18,
fontWeight:'600'
},
text: {
width: 200,
marginBottom: 20,
textAlign: 'center'
},
box_2: {
height: 45,
width: '75%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 25,
backgroundColor: 'red'
},
try_again: {
color: '#fff'
}
})
export default NetworkModal
Okay, what was that?
We imported the modal component from react-native and we also imported the NetInfo library. The idea is that, when a network request is about to be made, we first check whether or not the device is connected, if it is, we go ahead to make the request, if not, the modal is displayed with a button to try again. When users click on this button, we use the useNetInfo
hook to check whether or not the device is back online. If it is, we hide the modal and otherwise leave the modal displayed.
Next up, we need to make some adjustments to our Home
component, we will be making our network request from there. Add these lines of code.
const [title, setTitle] = useState('')
const {isConnected} = useContext(NetworkContext)
const getDummyTodos = async () => {
const res = await axios.get('https://jsonplaceholder.typicode.com/todos/1')
setTitle(res?.data?.title)
}
const handleFetch = () => {
if(isConnected){
setVisible(true)
return
}
getDummyTodos()
}
- We imported the NetworkContext to access the isConnected state that is handled by our
ParentComponent
. We'll be using the jsonplaceholder to get dummy todo text. - The getDummyTodos makes the call to our API
- The handleFetch function first checks to see if we are not connected, if the device is offline, we display the modal and return from the function immediately, to prevent other blocks of code from running. If the device is connected, the
isConnected
checks false, and the code block within the conditional is not evaluated, then the call of ourgetDummyTodos
function
And lastly, we need to make changes to the button to trigger the
handleFetch
function. Here's what changed.
<TouchableOpacity
onPress={handleFetch}
style={{
width: '90%',
height: 50,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'teal' //375027
}}
>
<Text style={{
color: 'white'
}}>
Get Dummy Todos
</Text>
</TouchableOpacity>
<Text>
Dummy to do: {title}
</Text>
Also not to forget, let's add the NetworkModal
to our home page
<NetworkModal
visible={visible}
setVisible={setVisible}
/>
Yeah, that's a lot of code. Here's what it looks like
So that's it.
Conclusion
The approach we used in handling network state is just one of the many approaches available. I decided to use this method in my projects because it offers flexibility and is easy to customize. Also, we can decide to add more functionality to our parent component as our project grows. Say, for example, we decide to prompt users to input their pin after they exit the app for some minutes, we can easily do that by using the AppState hook in our ParentComponent, and displaying the Pin modal after the set time is past. We can also prevent it from displaying on certain screens by checking for the route name.
The point is, that there is so much more we can do and more customization with this approach. Hope it helps. Cheers!!
This code can be found on Github
Top comments (0)