Using AbortController vs removeEventListener
Using AbortController
import { useEffect } from "react" export function useOnlineStatus(onlineCallback: () => void, offlineCallback: () => void) { useEffect(() => { const controller = new AbortController() window.addEventListener("online", onlineCallback, { signal: controller.signal }) window.addEventListener("offline", offlineCallback, { signal: controller.signal }) return () => { controller.abort() } }, [onlineCallback, offlineCallback]) }
vs
Using removeEventListener
import { useEffect } from 'react'; export function useOnlineStatus( onlineCallback: () => void, offlineCallback: () => void ) { useEffect(() => { window.addEventListener('online', onlineCallback); window.addEventListener('offline', offlineCallback); return () => { window.removeEventListener('online', onlineCallback); window.removeEventListener('offline', offlineCallback); }; }, [onlineCallback, offlineCallback]); }
Readability
The clean up function ( every re-render and unmounts) of the useEffect
can simply call controller.abort()
... return () => { controller.abort() } ...
Issue: Incorrect online status is rendered
For example,
Use the variable returns by the hook useOnlineStatus
in the component App
useOnlineStatus.ts
import { useEffect } from "react" export function useOnlineStatus(onlineCallback: () => void, offlineCallback: () => void) { useEffect(() => { const controller = new AbortController() window.addEventListener("online", onlineCallback, { signal: controller.signal }) window.addEventListener("offline", offlineCallback, { signal: controller.signal }) return () => { controller.abort() } }, [onlineCallback, offlineCallback]) // return the window online status return navigator.onLine; }
App.tsx
import React, {useCallback} from 'react'; import { useOnlineStatus } from './hook'; export function App(props) { const handleOnline = useCallback(() => { console.log('online'); }, []); const handleOffline = useCallback(() => { console.log('offline'); }, []); const isOnline = useOnlineStatus(handleOnline, handleOffline); return ( <div className='App'> <h1>Hello React.</h1> <h2>Start editing to see some magic happen!</h2> <p>Online status: <span>{isOnline.toString()}</span></p> </div> ); }
However, when switching between "Offline" and "No throttling" on the "Network" tab of Chrome DevTools, the component application does not display the correct online status isOnline
.
See that the value of isOnline
is false when the connection status is online:
Root cause
Because only when the component App is rendered, the return value navigator.onLine
when calling the hook useOnlineStatus
will be assigned to the variable isOnline
. However, when the online state changes, the component App will not re-render, which will show the old state of isOnline
instead of the current navigator.onLine
value in the window.
Solution
Update useOnlineStatus
hook to return state variable instead of navigator.onLine
.
When the online
event is delivered to the target (window), the callback functions onlineCallback
and set
are called to perform tasks and update the state variables respectively; when the offline
event is delivered to the target (window), the working principle is similar.
Declare the state varialbe isOnline
const [isOnline, setIsOnline] = useState(true);
Full codes
import { useEffect, useState, useCallback } from 'react'; export function useOnlineStatus( onlineCallback: () => void, offlineCallback: () => void ) { const [isOnline, setIsOnline] = useState(true); const handleOnline = useCallback(() => { onlineCallback(); setIsOnline(true); }, []); const handleOffline = useCallback(() => { offlineCallback(); setIsOnline(false); }, []); useEffect(() => { const controller = new AbortController(); window.addEventListener('online', handleOnline, { signal: controller.signal, }); window.addEventListener('offline', handleOffline, { signal: controller.signal, }); return () => { controller.abort(); }; }, [onlineCallback, offlineCallback]); return isOnline; }
In this way, when the online/offline status changes, the component App using the useOnlineStatus
hook will re-render and display the correct online status.
Top comments (2)
This is great code. Thank you for you trouble.
I wanna know about controller function in more detail.
Thank you :) You can take a look at this page to learn more if you are interested in developer.mozilla.org/en-US/docs/W...