Posted on 11/28/2021
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
.
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...).
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};
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? 😎
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.