React hook to detect online and offline network status and get network related info — useNetwork - Custom hooks #1

Vivek Joy
7 min readSep 15, 2021

--

Every web application requires to detect the network status of the user and also get network information to load the resources accordingly. This is achieved using web apis and it would be really beneficial if we can reuse this functionality at different places especially in a frontend framework like react. It helps us to show user feedback when network is offline, determine cache strategy for large assets etc. A common way to reuse a functionality is to create a custom hook. Here we will create such a hook called useNetwork which you can reuse anywhere in your react application.

Note: If you prefer to watch a video instead:

Project Setup

We will use create react app for the project setup.

npx create-react-app use-network-hook-example

we will use material-ui to style our application and use material icons for showing online offline icons. let’s install that using:

yarn add @material-ui/core @material-ui/icon

we will remove index.css, App.css, logo.svg setupTests.js, reportWebVitals.js and all its references from index.js and App.js.

Our final App.js will be:

import React from "react";const App = () => {return <>Hello React</>;};export default App;

We will create 2 new folders called components and hooks inside the src folder. The components of our application will go into the components folder and our custom hook will go into the hooks folder.

Creating useNetwork custom hook

Inside the hooks folder we will create a new file called useNetwork.js

first let’s create the useNetwork function declaration and export it

function useNetwork() {}export default useNetwork;

We will maintain a state for this hook to maintain the network state. The network state object will include the following values:

since — Time since the network became online or offline

online — online or offline network status

rtt — Returns the estimated effective round-trip time of the current connection, rounded to the nearest multiple of 25 milliseconds.

type — Returns the type of connection a device is using to communicate with the network. It will be one of the following values:

  • bluetooth
  • cellular
  • ethernet
  • none
  • wifi
  • wimax
  • other
  • unknown

saveData — Returns true if the user has set a reduced data usage option on the user agent.

downLink — Returns the effective bandwidth estimate in megabits per second, rounded to the nearest multiple of 25 kilobits per seconds.

downLinkMax — Returns the maximum downlink speed, in megabits per second (Mbps), for the underlying connection technology.

effectiveType — Returns the effective type of the connection meaning one of ‘slow-2g’, ‘2g’, ‘3g’, or ‘4g’. This value is determined using a combination of recently observed round-trip time and downlink values.

let’s declare the state variable using useState hook. let’s import useState first at the top of the file.

import { useState } from “react”;

now let’s declare the state variable inside useNetwork function:

const [state, setState] = useState(() => {
return {
since: undefined,
online: navigator.onLine,
...getNetworkConnectionInfo(),
};
});

as we see the above state uses a getNetworkConnectionInfo function to get the other network state properties than since and online. since is initially undefined and online will be set using navigator.onLine (it can be true or false according to network availability)

We will create two functions inside the useNetwork function , one is getNetworkConnection which we can reuse to get the network connection info. This doesn’t have great browser support but is available in chrome, chrome android and different other mobile browsers. we check for navigator.connection for its availability, for mozilla browsers we need to use navigator.mozConnection and navigator.webkitConnection for safari. If the browser doesn’t support any of it we will return null.

from getNetworkConnectionInfo we first declare a connection variable and call the getNetworkConnection to get the connection value and then we return the connection properties as an object.

function getNetworkConnection() {
return (
navigator.connection ||
navigator.mozConnection ||
navigator.webkitConnection ||
null
);
}
function getNetworkConnectionInfo() {
const connection = getNetworkConnection();
if (!connection) {
return {};
}
return {
rtt: connection.rtt,
type: connection.type,
saveData: connection.saveData,
downLink: connection.downLink,
downLinkMax: connection.downLinkMax,
effectiveType: connection.effectiveType,
};
}

now we need to register some listeners for online, offline and connection change events, which will help us in detecting online, offline and connection properties change and set the state accordingly.

we need a useEffect function which will run on the hook initialization. we will pass [] as the second argument to this useEffect to achieve that. and we will register online and offline listeners using window.addEventListener and connection change using connect.addEventListener and listen for change event.

useEffect(() => {
const handleOnline = () => {
setState((prevState) => ({
...prevState,
online: true,
since: new Date().toString(),
}));
};
const handleOffline = () => {
setState((prevState) => ({
...prevState,
online: false,
since: new Date().toString(),
}));
};
const handleConnectionChange = () => {
setState((prevState) => ({
...prevState,
...getNetworkConnectionInfo(),
}));
};
window.addEventListener("online", handleOnline);
window.addEventListener("offline", handleOffline);
const connection = getNetworkConnection();connection?.addEventListener("change", handleConnectionChange);return () => {
window.removeEventListener("online", handleOnline);
window.removeEventListener("offline", handleOffline);
connection?.removeEventListener("change", handleConnectionChange);
};
}, []);

We also added 3 handlers for the online, offline and change events.

The handleOnline function will set the online state to true and since to the current date, we will stringify for display purposes. also for others we will use previous state by using spread operator.

The handleOffline function will set the online state to false and since to the current date also for others we will use previous state.

The handleConnectionChange will use the previous state and will set the new state for connection properties using our getNetworkConnectionInfo function.

last we will return the state variable as the last statement in the function and our final useNetwork.js will look like this:

import { useState, useEffect } from "react";function getNetworkConnection() {
return (
navigator.connection ||
navigator.mozConnection ||
navigator.webkitConnection ||
null
);
}
function getNetworkConnectionInfo() {
const connection = getNetworkConnection();
if (!connection) {
return {};
}
return {
rtt: connection.rtt,
type: connection.type,
saveData: connection.saveData,
downLink: connection.downLink,
downLinkMax: connection.downLinkMax,
effectiveType: connection.effectiveType,
};
}
function useNetwork() {
const [state, setState] = useState(() => {
return {
since: undefined,
online: navigator.onLine,
...getNetworkConnectionInfo(),
};
});
useEffect(() => {
const handleOnline = () => {
setState((prevState) => ({
...prevState,
online: true,
since: new Date().toString(),
}));
};
const handleOffline = () => {
setState((prevState) => ({
...prevState,
online: false,
since: new Date().toString(),
}));
};
const handleConnectionChange = () => {
setState((prevState) => ({
...prevState,
...getNetworkConnectionInfo(),
}));
};
window.addEventListener("online", handleOnline);
window.addEventListener("offline", handleOffline);
const connection = getNetworkConnection();connection?.addEventListener("change", handleConnectionChange);return () => {
window.removeEventListener("online", handleOnline);
window.removeEventListener("offline", handleOffline);
connection?.removeEventListener("change", handleConnectionChange);
};
}, []);
return state;
}
export default useNetwork;

Creating our example component — UseNetworkExample.js

We will create a new file called UseNetworkExample.js in components folder.

we will import React, makeStyles from material ui for styling, Wifi and WifiOff icons from material ui icons, and Container and Typography components from material ui to organize our example. Also we will import our useNetwork hook.

We will create useStyles hook for creating classes using material ui by using makeStyles function. this useStyles hook we use inside our useNetworkExample and declare a classes variable to contain its values.

we will also create icon styles and styles for icon active and inactive.

Next we declare a networkState variable with which we will initialize our useNetwork hook.

we destructure the networkState to get the state related values for ease of use.

We will set the Container to maxWidth of ‘sm’ and we will add a div to enclose our example inside the Container and give it a class of container.

we use conditional operator to check for online value and show the Wifi icon if its true and WifiOff if its false.

Last we need a table to show the network stats where we will display all the connection related values, we use the NetworkInfoTable component for this purpose . For that we create a new file inside components folder called NetworkInfoTable.js. This NetworkInfoTable component will accept the network state as data props.

NetworkInfoTable component consists of a table and it accepts a data prop which will be our network state. we will map around the network state using Object.entries and display the key and values as a 2 column table.

NetworkInfoTable.js

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
const useStyles = makeStyles({
infoTable: {
borderCollapse: "collapse",
width: "100%",
"& td, th": {
border: "1px solid #dddddd",
textAlign: "left",
padding: 8,
},
"& td:nth-child(odd),th:nth-child(odd)": {
backgroundColor: "orange",
width: "40%",
},
},
});
const NetworkInfoTable = ({ data }) => {
const classes = useStyles();
return (
<table className={classes.infoTable}>
<thead>
<tr>
<th>Connection Property</th>
<th>Connection Value</th>
</tr>
</thead>
<tbody>
{Object.entries(data).map(([key, value]) => (
<tr key={key}>
<td>{key}</td>
<td>{value?.toString()}</td>
</tr>
))}
</tbody>
</table>
);
};
export default NetworkInfoTable;

NetworkExample.js

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Wifi from "@material-ui/icons/Wifi";
import WifiOff from "@material-ui/icons/WifiOff";
import useNetwork from "../../hooks/useNetwork";
import { Container, Typography } from "@material-ui/core";
import NetworkInfoTable from "./NetworkInfoTable";
const useStyles = makeStyles({
container: {
display: "flex",
flexDirection: "column",marginTop: 20,
},
icon: {
fontSize: 100,
margin: "0 auto",
},
active: {
color: "green",
},
inactive: {
color: "grey",
},
});
const UseNetworkExample = () => {
const classes = useStyles();
const iconActiveStyles = `${classes.icon} ${classes.active}`;
const iconInactiveStyles = `${classes.icon} ${classes.inactive}`;
const networkState = useNetwork();
const {
online,
since,
downLink,
downLinkMax,
effectiveType,
rtt,
saveData,
type,
} = networkState;
return (
<Container maxWidth="md">
<div className={classes.container}>
{online ? (
<Wifi className={iconActiveStyles} />
) : (
<WifiOff className={iconInactiveStyles} />
)}
<div>
<h1>Network Stats:</h1>
<NetworkInfoTable data={networkState} />
</div>
</div>
</Container>
);
};
export default UseNetworkExample;

Conclusion

We have successfully created a reusable hook called useNetwork with which we can detect browser network status and network connection information.

Running on chrome android:-

--

--

Vivek Joy

Javascript developer at heart. React developer by profession.