Custom React Hooks: useNetworkState

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'll create a hook 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 toast message informing it to check its network connectivity. To do this in a React app, here's how you could proceed:

const App = () => {
  const [isOnline, setIsOnline] = useState(window.navigator.onLine);

  useEffect(() => {
    const handleOnline = () => {
      setIsOnline(true);
    };
    const handleOffline = () => {
      setIsOnline(false);
    };
    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  return (
    <div>
      <h1>My Awesome App</h1>
      <p>
        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Culpa
        provident tenetur molestias fugiat expedita quaerat dolores dignissimos
        dicta, error amet reiciendis voluptates delectus perspiciatis dolorum
        saepe, sunt, similique vitae illo.
      </p>
      {!isOnline && (
        <div className="toast">
          You are offline. Please check your connectivity and try again.
        </div>
      )}
    </div>
  );
};

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:

const 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:

const useNetworkState = () => {
  const [isOnline, setIsOnline] = useBoolean(window.navigator.onLine);

  useEffect(() => {
    window.addEventListener('online', setIsOnline.on);
    window.addEventListener('offline', setIsOnline.off);

    return () => {
      window.removeEventListener('online', setIsOnline.on);
      window.removeEventListener('offline', setIsOnline.off);
    };
  }, []);

  return isOnline;
};

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 link, that will bring you to my article on the useBoolean hook.

Note: if you don't want to use the useBoolean hook, you can be satisfied with the native useState one, and useNetworkState would be the following:

const useNetworkState = () => {
  const [isOnline, setIsOnline] = useState(window.navigator.onLine);

  useEffect(() => {
    const handleOnline = () => {
      setIsOnline(true);
    };
    const handleOffline = () => {
      setIsOnline(false);
    };
    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  return isOnline;
};

Usage

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

const App = () => {
  const isOnline = useNetworkState()

  return (
    <div>
      <h1>My Awesome App</h1>
      <p>
        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Culpa
        provident tenetur molestias fugiat expedita quaerat dolores dignissimos
        dicta, error amet reiciendis voluptates delectus perspiciatis dolorum
        saepe, sunt, similique vitae illo.
      </p>
      {!isOnline && (
        <div className="toast">
          You are offline. Please check your connectivity and try again.
        </div>
      )}
    </div>
  );
};

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

All the logic is now abstracted outside of the component, that only focuses on what matters to it. By doing this, we're following the SOC (Separation of Concerns) design principle — more information here.

Conclusion

I hope this hook will be useful to you for your projects. If you have any questions, feel free to ask them in the comments section. For now, thanks for reading me, and see you next time for a new custom hook. 🤗

Source code available on CodeSanbox.

Support Me

If you wish to support me, you can click the following link to buy me a coffee (which I will then probably turn into a new custom hook... ☕).

17