← back to Watches
src/hooks/useAutoRefresh.js
104 lines
import { useState, useEffect, useCallback, useRef } from 'react';
/**
* useAutoRefresh - Custom hook for automatic data polling with visibility awareness
* @param {function} refreshFn - Function to call for refreshing data (should return a Promise)
* @param {number} interval - Polling interval in milliseconds (default: 5 minutes)
* @param {boolean} enabled - Whether auto-refresh is enabled (default: true)
* @returns {Object} { isRefreshing, lastRefresh, refresh, error }
*/
export default function useAutoRefresh(refreshFn, interval = 5 * 60 * 1000, enabled = true) {
const [isRefreshing, setIsRefreshing] = useState(false);
const [lastRefresh, setLastRefresh] = useState(null);
const [error, setError] = useState(null);
const intervalRef = useRef(null);
const refreshFnRef = useRef(refreshFn);
const isVisibleRef = useRef(true);
// Keep refreshFn ref updated
useEffect(() => {
refreshFnRef.current = refreshFn;
}, [refreshFn]);
// Manual refresh function
const refresh = useCallback(async () => {
if (isRefreshing) return;
setIsRefreshing(true);
setError(null);
try {
if (refreshFnRef.current) {
await refreshFnRef.current();
}
setLastRefresh(Date.now());
} catch (err) {
console.error('Auto-refresh error:', err);
setError(err);
} finally {
setIsRefreshing(false);
}
}, [isRefreshing]);
// Handle visibility change
useEffect(() => {
const handleVisibilityChange = () => {
isVisibleRef.current = document.visibilityState === 'visible';
if (isVisibleRef.current && enabled) {
// Tab became visible - check if we need to refresh
const timeSinceLastRefresh = lastRefresh ? Date.now() - lastRefresh : Infinity;
if (timeSinceLastRefresh >= interval) {
refresh();
}
}
};
document.addEventListener('visibilitychange', handleVisibilityChange);
return () => {
document.removeEventListener('visibilitychange', handleVisibilityChange);
};
}, [enabled, interval, lastRefresh, refresh]);
// Set up polling interval
useEffect(() => {
if (!enabled) {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
return;
}
// Initial refresh
if (!lastRefresh) {
refresh();
}
// Set up interval
intervalRef.current = setInterval(() => {
// Only refresh if tab is visible
if (isVisibleRef.current) {
refresh();
}
}, interval);
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
};
}, [enabled, interval, refresh, lastRefresh]);
return {
isRefreshing,
lastRefresh,
refresh,
error
};
}