2025-03-29 16:50:39 +08:00
|
|
|
|
"use client"
|
|
|
|
|
|
|
2025-07-07 17:08:27 +08:00
|
|
|
|
import { useState, useEffect, useMemo } from "react"
|
2025-03-29 16:50:39 +08:00
|
|
|
|
import type { Device } from "@/components/device-grid"
|
2025-07-07 17:08:27 +08:00
|
|
|
|
import { deviceApi } from "@/lib/api/devices"
|
|
|
|
|
|
import type { DeviceStatus } from "@/types/device"
|
2025-03-29 16:50:39 +08:00
|
|
|
|
|
2025-07-07 17:08:27 +08:00
|
|
|
|
export function useDeviceStatusPolling(devices: Device[], interval = 30000) {
|
|
|
|
|
|
const [statuses, setStatuses] = useState<Record<string, DeviceStatus>>({})
|
|
|
|
|
|
const [isLoading, setIsLoading] = useState(false)
|
|
|
|
|
|
const [error, setError] = useState<string | null>(null)
|
2025-03-29 16:50:39 +08:00
|
|
|
|
|
2025-07-07 17:08:27 +08:00
|
|
|
|
// 使用 useMemo 序列化 device IDs,确保依赖项的稳定性
|
|
|
|
|
|
const deviceIds = useMemo(
|
|
|
|
|
|
() =>
|
|
|
|
|
|
devices
|
|
|
|
|
|
.map((d) => d.id)
|
|
|
|
|
|
.sort()
|
|
|
|
|
|
.join(","),
|
|
|
|
|
|
[devices],
|
2025-03-29 16:50:39 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
2025-07-07 17:08:27 +08:00
|
|
|
|
if (!deviceIds) {
|
|
|
|
|
|
setStatuses({})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let isActive = true // 标记,防止在组件卸载后仍更新状态
|
|
|
|
|
|
const idArray = deviceIds.split(",")
|
|
|
|
|
|
|
2025-03-29 16:50:39 +08:00
|
|
|
|
const pollStatus = async () => {
|
2025-07-07 17:08:27 +08:00
|
|
|
|
setIsLoading(true)
|
|
|
|
|
|
setError(null)
|
2025-03-29 16:50:39 +08:00
|
|
|
|
try {
|
2025-07-07 17:08:27 +08:00
|
|
|
|
const response = await deviceApi.checkStatus(idArray)
|
|
|
|
|
|
if (isActive && response.data) {
|
|
|
|
|
|
setStatuses((prev) => ({ ...prev, ...response.data }))
|
|
|
|
|
|
} else if (response.message) {
|
|
|
|
|
|
throw new Error(response.message)
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
if (isActive) {
|
|
|
|
|
|
setError(e instanceof Error ? e.message : "获取设备状态失败")
|
|
|
|
|
|
}
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
if (isActive) {
|
|
|
|
|
|
setIsLoading(false)
|
|
|
|
|
|
}
|
2025-03-29 16:50:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-07 17:08:27 +08:00
|
|
|
|
// 立即执行一次
|
|
|
|
|
|
pollStatus()
|
2025-03-29 16:50:39 +08:00
|
|
|
|
|
2025-07-07 17:08:27 +08:00
|
|
|
|
// 设置定时轮询
|
|
|
|
|
|
const intervalId = setInterval(pollStatus, interval)
|
2025-03-29 16:50:39 +08:00
|
|
|
|
|
2025-07-07 17:08:27 +08:00
|
|
|
|
// 监听页面可见性,实现智能轮询
|
|
|
|
|
|
const handleVisibilityChange = () => {
|
|
|
|
|
|
if (document.hidden) {
|
|
|
|
|
|
clearInterval(intervalId) // 页面隐藏时暂停
|
|
|
|
|
|
} else {
|
|
|
|
|
|
pollStatus() // 页面可见时立即更新并重新开始轮询
|
|
|
|
|
|
setInterval(pollStatus, interval)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-03-29 16:50:39 +08:00
|
|
|
|
|
2025-07-07 17:08:27 +08:00
|
|
|
|
document.addEventListener("visibilitychange", handleVisibilityChange)
|
|
|
|
|
|
|
|
|
|
|
|
// 清理函数
|
|
|
|
|
|
return () => {
|
|
|
|
|
|
isActive = false
|
|
|
|
|
|
clearInterval(intervalId)
|
|
|
|
|
|
document.removeEventListener("visibilitychange", handleVisibilityChange)
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [deviceIds, interval]) // 依赖项现在是稳定的字符串
|
|
|
|
|
|
|
|
|
|
|
|
return { statuses, isLoading, error }
|
|
|
|
|
|
}
|