import { useState, useEffect, useRef, useCallback } from 'react'
export const usePolling = (
fetchDataCallback: () => Promise<unknown>,
interval: number,
enabled: boolean = true
) => {
const [loading, setLoading] = useState(true)
const intervalRef = useRef<number | null>(null)
const isFetching = useRef(false)
const mounted = useRef(false)
const enabledRef = useRef(enabled)
const setEnabled = useCallback((enabled: boolean) => {
console.log('setEnabled', enabled)
enabledRef.current = enabled
if (enabled) {
startPolling()
} else {
stopPolling()
}
}, [])
const fetchData = useCallback(async () => {
if (isFetching.current) return
isFetching.current = true
try {
await fetchDataCallback()
} catch (error) {
console.error('Failed to fetch data in usePolling:', error)
} finally {
if (mounted.current) {
setLoading(false)
isFetching.current = false
}
}
}, [fetchDataCallback])
useEffect(() => {
mounted.current = true
return () => {
mounted.current = false
}
}, [])
const stopPolling = () => {
if (intervalRef.current) {
window.clearTimeout(intervalRef.current)
intervalRef.current = null
}
}
const startPolling = () => {
stopPolling()
if (document.hidden || !enabledRef.current) {
return
}
intervalRef.current = window.setTimeout(async () => {
await fetchData()
startPolling()
}, interval)
}
useEffect(() => {
const handleVisibilityChange = () => {
if (document.hidden) {
stopPolling()
} else if (enabledRef.current) {
if (!isFetching.current) setLoading(true)
fetchData()
startPolling()
}
}
if (enabledRef.current) {
if (!isFetching.current) setLoading(true)
fetchData()
startPolling()
document.addEventListener('visibilitychange', handleVisibilityChange)
} else {
stopPolling()
}
return () => {
stopPolling()
document.removeEventListener('visibilitychange', handleVisibilityChange)
}
}, [fetchData, interval])
return { loading, setEnabled, enabled: enabledRef }
}
- 链式轮询
- 基于react的hook,更改轮询函数参数时,会自动更新轮询方法
应用示例