usePolling
All about usePolling hook in React
A reusable polling hook to run an async function at a specified interval.
Usage
import React from "react";
import { usePolling } from "./usePolling";
async function fetchServerStatus() {
const res = await fetch("/api/status");
if (!res.ok) throw new Error("Failed to fetch status");
return res.json();
}
export function ServerStatusChecker() {
const {
data,
error,
isRunning,
start,
stop
} = usePolling(fetchServerStatus, {
interval: 30 * 60 * 1000, // 30 minutes
immediate: true,
autoStart: true,
});
return (
<div>
<h2>Server Status</h2>
{error && <p>Error: {String(error)}</p>}
{data && <pre>{JSON.stringify(data, null, 2)}</pre>}
<p>Polling: {isRunning ? "Running" : "Stopped"}</p>
<button onClick={start} disabled={isRunning}>
Start
</button>
<button onClick={stop} disabled={!isRunning}>
Stop
</button>
</div>
);
}
Hook
import { useCallback, useEffect, useRef, useState } from "react";
interface UsePollingOptions {
/** interval in ms */
interval: number;
/** run once immediately on start */
immediate?: boolean;
/** start automatically when hook mounts */
autoStart?: boolean;
}
type AsyncFunction<T> = () => Promise<T>;
/**
* A reusable polling hook to run an async function at a specified interval.
* @param asyncFn The async function to execute on each poll.
* @param options Polling configuration.
*/
export function usePolling<T>(
asyncFn: AsyncFunction<T>,
{ interval, autoStart = true, immediate = false }: UsePollingOptions
) {
const [isRunning, setIsRunning] = useState(autoStart);
const [data, setData] = useState<T | null>(null);
const [error, setError] = useState<unknown>(null);
const timerRef = useRef<NodeJS.Timeout | null>(null);
const start = useCallback(() => {
if (!isRunning) setIsRunning(true);
}, [isRunning]);
const stop = useCallback(() => {
setIsRunning(false);
if (timerRef.current) {
clearTimeout(timerRef.current);
timerRef.current = null;
}
}, []);
const execute = useCallback(async () => {
try {
const result = await asyncFn();
setData(result);
setError(null);
} catch (err) {
setError(err);
// Log silently to avoid breaking the loop
console.error("[usePolling] Polling error:", err);
}
}, [asyncFn]);
// Core polling loop
const tick = useCallback(async () => {
await execute();
timerRef.current = setTimeout(tick, interval);
}, [execute, interval]);
// Manage start/stop lifecycle
useEffect(() => {
if (isRunning) {
if (immediate) {
execute();
}
timerRef.current = setTimeout(tick, immediate ? interval : 0);
}
return () => {
if (timerRef.current) clearTimeout(timerRef.current);
};
}, [isRunning, tick, immediate, execute, interval]);
return { data, error, isRunning, start, stop };
}
Parameters
Name | Type | Default | Description |
---|---|---|---|
asyncFn | () => Promise<T> | — | The async function to execute on each poll. Must return a promise. |
options | { interval: number; immediate?: boolean; autoStart?: boolean; } | — | Configuration object for polling. |
options.interval | number | — | Polling interval in milliseconds. |
options.autoStart | boolean | true | If true , polling starts automatically on mount. |
options.immediate | boolean | false | If true , the async function runs immediately before the first interval. |
Returns
Name | Type | Description |
---|---|---|
data | T | null | Latest successful result returned by the async function. |
error | unknown | Last error thrown by the async function, if any. |
isRunning | boolean | Whether polling is currently active. |
start | () => void | Function to manually start polling. |
stop | () => void | Function to manually stop polling. |