src/usePolling.ts (93 lines of code) (raw):
import {
useState,
useCallback,
useRef,
useEffect
} from 'react'
import { requestAnimationFrameInterval } from './requestAnimationFrameInterval'
import { usePrevious } from './usePrevious'
interface UsePollingProps {
callback: () => void;
condition: boolean;
interval: number;
onPollingFailed?: () => void;
onPollingSucceed?: () => void;
}
interface UsePollingApi {
isPolling: boolean;
restartPolling: () => void;
startPolling: () => void;
stopPolling: () => void;
}
const usePolling = ({
callback,
condition,
interval,
onPollingFailed,
onPollingSucceed
}: UsePollingProps): UsePollingApi => {
const abort = useRef<(() => void) | null>(null)
const [isPolling, setIsPolling] = useState<boolean>(false)
const prevCondition = usePrevious<boolean>(condition)
const terminateInterval = useCallback((): void => {
if (!abort?.current) {
return
}
abort.current()
abort.current = null
}, [abort])
const stopPolling = useCallback((): void => {
terminateInterval()
setIsPolling(false)
}, [terminateInterval])
const startPolling = useCallback(() => {
setIsPolling(true)
abort.current = requestAnimationFrameInterval(
async () => {
try {
await callback()
} catch {
onPollingFailed?.()
stopPolling()
}
},
interval
)
}, [
stopPolling,
callback,
interval,
onPollingFailed
])
const restartPolling = useCallback(() => {
stopPolling()
startPolling()
}, [stopPolling, startPolling])
useEffect(() => {
if (condition) {
startPolling()
}
if (!condition && prevCondition) {
onPollingSucceed?.()
stopPolling()
}
return stopPolling
}, [
startPolling,
stopPolling,
condition,
prevCondition,
onPollingSucceed
])
return {
isPolling,
restartPolling,
startPolling,
stopPolling
}
}
export {
usePolling,
UsePollingProps,
UsePollingApi
}