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 就会重新渲染并显示正确的在线状态。