React - 使用 AbortController 和 addEventListener 创建自定义钩子来处理在线和离线事件
使用 AbortController 与 removeEventListener
使用 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])
}对比
使用 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]);
}可读性
`useEffect` 的清理函数(每次重新渲染和卸载)可以简单地调用 `controller.abort()`
...
return () => {
controller.abort()
}
...问题:呈现错误的在线状态
例如,
使用组件 App 中钩子 useOnlineStatus 返回的变量
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;
}应用程序.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 (
Hello React.
Start editing to see some magic happen!
Online status: {isOnline.toString()}
);
}但是,在 Chrome DevTools 的“网络”标签上切换“离线”和“无限制”时,组件应用程序不会显示正确的在线状态“isOnline”。
看到当连接状态为在线时,`isOnline`的值为false:

根本原因
因为只有当组件 App 被渲染时,调用钩子 `useOnlineStatus` 的返回值 `navigator.onLine` 才会赋值给变量 `isOnline`。但是当在线状态改变时,组件 App 并不会重新渲染,窗口中显示的会是旧状态的 `isOnline`,而不是当前的 `navigator.onLine` 值。
解决方案
更新“useOnlineStatus”钩子以返回状态变量而不是“navigator.onLine”。
当向目标(窗口)传递`online`事件时,会分别调用回调函数`onlineCallback`和`set`执行任务、更新状态变量;当向目标(窗口)传递`offline`事件时,工作原理类似。
声明状态变量“isOnline”
const [isOnline, setIsOnline] = useState(true);
完整代码
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;
}这样,当在线/离线状态发生变化时,使用 `useOnlineStatus` hook 的组件 App 就会重新渲染并显示正确的在线状态。