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 (0)