DEV Community

olaseinde AYOWALE
olaseinde AYOWALE

Posted on

React native Netinfo: Detecting Internet connectivity in react native.

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 
Enter fullscreen mode Exit fullscreen mode

We need these libraries to create and manage the app's navigationRead more

npm install axios
Enter fullscreen mode Exit fullscreen mode

For making our network request, we'll use the library axios and the NetInfo library.

npm install @react-native-community/netinfo
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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>
    )
}
Enter fullscreen mode Exit fullscreen mode

What did we just do?

  1. We need to first import our useEffect hoot from react, we need this because, we have to leverage the useEffect clean-up function, which works typically like the componentDidUnmount, calling our unsubscribe function in the cleanup function.
  2. 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>
}
Enter fullscreen mode Exit fullscreen mode

What did we just do?

  1. 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 the isConnected and the setConnected 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>
    )
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

What just happened?

  1. We imported our ParentComponent, Wrapping our HomePage component as its child, that way, we can display the NoInternet component when the device is offline
  2. 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
Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Add the line of code to the returned statement in the ParentComponent

   {isOffline && <NoInternet />}
Enter fullscreen mode Exit fullscreen mode
  1. What was that? We imported the NoInternet component and based on the isOffline state in the ParentComponent, we display the NoInternet.js component Great, we can not test our implementation

Final Output

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
Enter fullscreen mode Exit fullscreen mode

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()
    }

Enter fullscreen mode Exit fullscreen mode
  1. 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.
  2. The getDummyTodos makes the call to our API
  3. 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 our getDummyTodos 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>
Enter fullscreen mode Exit fullscreen mode

Also not to forget, let's add the NetworkModal to our home page

<NetworkModal
      visible={visible}
      setVisible={setVisible}
 />
Enter fullscreen mode Exit fullscreen mode

Yeah, that's a lot of code. Here's what it looks like
Final Output

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

Oldest comments (0)