Logo
Cover image

Custom React Hooks: useNetworkState

Posted on 11/28/2021

reactjavascriptprogramming

In the last episode of the Custom React Hooks series, we've implemented the useLocalStorage hook to simplify local storage management. In today's episode, we will create one to simplify the observation of our users network state: useNetworkState.

Motivation

Let's say you're building an application that requires to be online in order to work correctly. If the user gets disconnected, you want to display a message informing them to check their network connectivity. To do this in a React app, here's how you could proceed:

1const App = () => {
2  const [isOnline, setIsOnline] = useState(window.navigator.onLine);
3
4  useEffect(() => {
5    const handleOnline = () => setIsOnline(true);
6    const handleOffline = () => setIsOnline(false);
7
8    window.addEventListener('online', handleOnline);
9    window.addEventListener('offline', handleOffline);
10
11    return () => {
12      window.removeEventListener('online', handleOnline);
13      window.removeEventListener('offline', handleOffline);
14    };
15  }, []);
16
17  return (
18    <div>
19      <h1>My Awesome App</h1>
20      <p>
21        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Culpa
22        provident tenetur molestias fugiat expedita quaerat dolores dignissimos
23        dicta, error amet reiciendis voluptates delectus perspiciatis dolorum
24        saepe, sunt, similique vitae illo.
25      </p>
26      {!isOnline && (
27        <div className="toast">
28          You are offline. Please check your connectivity and try again.
29        </div>
30      )}
31    </div>
32  );
33};

This works fine, but this is already a lot of code, and above all, a lot of logic just inside the useEffect hook. Our goal is to define a useNetworkState hook that will abstract this logic inside a custom hook, that is reusable over the entire app to listen for network state changes. This will also reduce the code inside our App component, that could quickly get longer and longer if we add some other logic (click listeners, form submission, keyboard listeners...).

Implementation

As always, let's think about the interface of our hook (how we are going to use it). In our case, we could have something as simple as this one-liner:

1const isOnline = useNetworkState()

Pretty straightforward. This hook would return a single boolean value that gets updated accordingly to synchronize with the network status.

We can already dive into the hook's implementation, by only extracting the logic we've written in the useEffect hook of our App component. At the end, the hook will look like this:

1const useNetworkState = () => {
2  const [isOnline, setIsOnline] = useBoolean(window.navigator.onLine);
3
4  useEffect(() => {
5    window.addEventListener('online', setIsOnline.on);
6    window.addEventListener('offline', setIsOnline.off);
7
8    return () => {
9      window.removeEventListener('online', setIsOnline.on);
10      window.removeEventListener('offline', setIsOnline.off);
11    };
12  }, []);
13
14  return isOnline;
15};

Wait, what the heck is useBoolean? This hook doesn't exist... 🤨

Yes, you're right. However, if you've been following this series from the very first episode, this hook might remind you something, as it is the first custom hook we've implemented! If you've discovered this series on the way, no problem: just head over to this article to learn about this useBoolean hook.

If you don't want to use the useBoolean hook, you are free to use the native useState one.

1const useNetworkState = () => {
2  const [isOnline, setIsOnline] = useState(window.navigator.onLine);
3
4  useEffect(() => {
5    const handleOnline = () => setIsOnline(true);
6    const handleOffline = () => setIsOnline(false);
7
8    window.addEventListener('online', handleOnline);
9    window.addEventListener('offline', handleOffline);
10
11    return () => {
12      window.removeEventListener('online', handleOnline);
13      window.removeEventListener('offline', handleOffline);
14    };
15  }, []);
16
17  return isOnline;
18};

Usage

Back to our App component, where we can drastically simplify the code (see by yourself):

1const App = () => {
2  const isOnline = useNetworkState()
3
4  return (
5    <div>
6      <h1>My Awesome App</h1>
7      <p>
8        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Culpa
9        provident tenetur molestias fugiat expedita quaerat dolores dignissimos
10        dicta, error amet reiciendis voluptates delectus perspiciatis dolorum
11        saepe, sunt, similique vitae illo.
12      </p>
13      {!isOnline && (
14        <div className="toast">
15          You are offline. Please check your connectivity and try again.
16        </div>
17      )}
18    </div>
19  );
20};

Yes, yes. One-line. Awesome, right? 😎

Wrapping Up

We have abstracted all the network logic outside the component, which now only focuses on what matters to it. By doing this, we're following the SOC (Separation of Concerns) design principle — more information here. I hope this hook will be useful to you for your projects. In the next article, we are going to implement a hook for dealing with audios and sound effects: useAudio.


Source code available on CodeSandbox.

Copyright © 2022 Ludovic CHOMBEAU