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,更改轮询函数参数时,会自动更新轮询方法

应用示例