React native Netinfo: Detecting Internet connectivity in react native.
Tags: javascript, react, reactnative, android
Published: 2022-09-04 14:52:14

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](https://github.com/react-native-netinfo/react-native-netinfo#readme)

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,

```javascript
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 navigation[Read more] (https://reactnavigation.org/docs/getting-started/)

```javascript
npm install axios
```

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

```javascript
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;

 ```javascript
{
  "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:

```javascript
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?
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](https://reactjs.org/docs/context.html)

Firstly, let's create a file and call it NetworkContext.js

```javascript
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?
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.

```javascript
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

```javascript
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

```javascript
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?
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`

```javascript
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](https://reactnavigation.org/docs/getting-started/)

```javascript
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.

```javascript
import NoInternet from "./NoInternet"; //import the NoInternet Component
```
Add the line of code to the returned statement in the `ParentComponent`
```javascript
   {isOffline && <NoInternet />}
```
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](https://res.cloudinary.com/dquiwka6j/image/upload/v1658070801/miwdff99wt7qk2avyewy.gif)

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.

```javascript
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.

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

```
1. We imported the NetworkContext to access the isConnected state that is handled by our `ParentComponent`. We'll be using the [jsonplaceholder](https://jsonplaceholder.typicode.com/) 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.

```javascript
<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

```javascript
<NetworkModal
      visible={visible}
      setVisible={setVisible}
 />
```
Yeah, that's a lot of code. Here's what it looks like
![Final Output](https://res.cloudinary.com/dquiwka6j/image/upload/v1658074618/adyno6zyelyo67wdad0x.gif)

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](https://github.com/Ayowalexy/tutorial)