Implement visibility-aware polling to reduce background tab resource usage
- Add usePageVisibility hook to track page visibility state - Add pauseWhenHidden option to usePolledData (defaults to false for backward compatibility) - When enabled, polling pauses when page is hidden and resumes with immediate refresh when visible - Refactor useBlocklistStatus to use usePolledData with pauseWhenHidden=true - Add comprehensive tests for usePageVisibility hook - Add polling lifecycle documentation to Web-Development.md Fixes #36: Polling continues when tab is not visible Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
* React hook for polling blocklist schedule error state.
|
||||
*/
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { usePolledData } from "./usePolledData";
|
||||
import { fetchSchedule } from "../api/blocklist";
|
||||
|
||||
const BLOCKLIST_POLL_INTERVAL_MS = 60_000;
|
||||
@@ -14,36 +14,17 @@ export interface UseBlocklistStatusReturn {
|
||||
/**
|
||||
* Poll `GET /api/blocklists/schedule` every 60 seconds to detect whether
|
||||
* the most recent blocklist import had errors.
|
||||
*
|
||||
* Polling pauses when the page is hidden and resumes immediately when visible.
|
||||
*/
|
||||
export function useBlocklistStatus(): UseBlocklistStatusReturn {
|
||||
const [hasErrors, setHasErrors] = useState(false);
|
||||
const abortRef = useRef<AbortController | null>(null);
|
||||
const { data } = usePolledData({
|
||||
fetcher: (signal) => fetchSchedule(signal),
|
||||
selector: (response) => response.last_run_errors === true,
|
||||
errorMessage: "Failed to fetch blocklist schedule",
|
||||
pollInterval: BLOCKLIST_POLL_INTERVAL_MS,
|
||||
pauseWhenHidden: true,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const poll = (): void => {
|
||||
abortRef.current?.abort();
|
||||
const controller = new AbortController();
|
||||
abortRef.current = controller;
|
||||
|
||||
fetchSchedule(controller.signal)
|
||||
.then((info) => {
|
||||
if (controller.signal.aborted) {
|
||||
return;
|
||||
}
|
||||
setHasErrors(info.last_run_errors === true);
|
||||
})
|
||||
.catch(() => {
|
||||
// Silently swallow network errors — do not change indicator state.
|
||||
});
|
||||
};
|
||||
|
||||
poll();
|
||||
const id = window.setInterval(poll, BLOCKLIST_POLL_INTERVAL_MS);
|
||||
return (): void => {
|
||||
abortRef.current?.abort();
|
||||
window.clearInterval(id);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { hasErrors };
|
||||
return { hasErrors: data ?? false };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user