| import { useEffect, useRef } from "react"; | |
| /** | |
| * usePolling — self-rescheduling setTimeout-based polling. | |
| * | |
| * Calls `callback` every `interval` ms while `active` is true. | |
| * Stops automatically when `active` becomes false. | |
| * Cleans up on unmount. | |
| * | |
| * Uses setTimeout (not setInterval) so each poll waits for the | |
| * previous one to finish before scheduling the next — identical | |
| * to the pattern used in Documents.jsx. | |
| * | |
| * @param {() => Promise<void>} callback Async function to call each tick. | |
| * @param {number} interval Milliseconds between polls. | |
| * @param {boolean} active Poll only while this is true. | |
| */ | |
| export function usePolling(callback, interval, active) { | |
| const timerRef = useRef(null); | |
| const callbackRef = useRef(callback); | |
| // Keep callbackRef current without restarting the effect. | |
| useEffect(() => { | |
| callbackRef.current = callback; | |
| }, [callback]); | |
| useEffect(() => { | |
| if (!active) { | |
| clearTimeout(timerRef.current); | |
| timerRef.current = null; | |
| return; | |
| } | |
| timerRef.current = setTimeout(async () => { | |
| await callbackRef.current(); | |
| }, interval); | |
| return () => { | |
| clearTimeout(timerRef.current); | |
| timerRef.current = null; | |
| }; | |
| // Re-run whenever active or the data that drives active changes. | |
| // interval is stable so including it has no cost. | |
| }, [active, interval]); | |
| } | |