Fix intermittent page reloads from VersionDisplay reconnect logic
- Add guards to prevent reload when not updating - Use refs to track isUpdating and isNetworkError state in interval callbacks - Add hasReloadedRef flag to prevent multiple reloads - Clear reconnect interval when update completes or component unmounts - Only start reconnect attempts when actually updating - Prevents false positive reloads when server responds normally
This commit is contained in:
@@ -87,6 +87,9 @@ export function VersionDisplay({ onOpenReleaseNotes }: VersionDisplayProps = {})
|
|||||||
const [updateStartTime, setUpdateStartTime] = useState<number | null>(null);
|
const [updateStartTime, setUpdateStartTime] = useState<number | null>(null);
|
||||||
const lastLogTimeRef = useRef<number>(Date.now());
|
const lastLogTimeRef = useRef<number>(Date.now());
|
||||||
const reconnectIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
const reconnectIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
const hasReloadedRef = useRef<boolean>(false);
|
||||||
|
const isUpdatingRef = useRef<boolean>(false);
|
||||||
|
const isNetworkErrorRef = useRef<boolean>(false);
|
||||||
|
|
||||||
const executeUpdate = api.version.executeUpdate.useMutation({
|
const executeUpdate = api.version.executeUpdate.useMutation({
|
||||||
onSuccess: (result) => {
|
onSuccess: (result) => {
|
||||||
@@ -155,21 +158,40 @@ export function VersionDisplay({ onOpenReleaseNotes }: VersionDisplayProps = {})
|
|||||||
|
|
||||||
// Attempt to reconnect and reload page when server is back
|
// Attempt to reconnect and reload page when server is back
|
||||||
const startReconnectAttempts = () => {
|
const startReconnectAttempts = () => {
|
||||||
if (reconnectIntervalRef.current) return;
|
// Only start if we're actually updating and haven't already started
|
||||||
|
if (reconnectIntervalRef.current || !isUpdatingRef.current || hasReloadedRef.current) return;
|
||||||
|
|
||||||
setUpdateLogs(prev => [...prev, 'Attempting to reconnect...']);
|
setUpdateLogs(prev => [...prev, 'Attempting to reconnect...']);
|
||||||
|
|
||||||
reconnectIntervalRef.current = setInterval(() => {
|
reconnectIntervalRef.current = setInterval(() => {
|
||||||
void (async () => {
|
void (async () => {
|
||||||
|
// Guard: Only proceed if we're still updating and in network error state
|
||||||
|
if (!isUpdatingRef.current || !isNetworkErrorRef.current || hasReloadedRef.current) {
|
||||||
|
// Clear interval if we're no longer updating
|
||||||
|
if (!isUpdatingRef.current && reconnectIntervalRef.current) {
|
||||||
|
clearInterval(reconnectIntervalRef.current);
|
||||||
|
reconnectIntervalRef.current = null;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Try to fetch the root path to check if server is back
|
// Try to fetch the root path to check if server is back
|
||||||
const response = await fetch('/', { method: 'HEAD' });
|
const response = await fetch('/', { method: 'HEAD' });
|
||||||
if (response.ok || response.status === 200) {
|
if (response.ok || response.status === 200) {
|
||||||
|
// Double-check we're still updating before reloading
|
||||||
|
if (!isUpdatingRef.current || hasReloadedRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark that we're about to reload to prevent multiple reloads
|
||||||
|
hasReloadedRef.current = true;
|
||||||
setUpdateLogs(prev => [...prev, 'Server is back online! Reloading...']);
|
setUpdateLogs(prev => [...prev, 'Server is back online! Reloading...']);
|
||||||
|
|
||||||
// Clear interval and reload
|
// Clear interval and reload
|
||||||
if (reconnectIntervalRef.current) {
|
if (reconnectIntervalRef.current) {
|
||||||
clearInterval(reconnectIntervalRef.current);
|
clearInterval(reconnectIntervalRef.current);
|
||||||
|
reconnectIntervalRef.current = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -183,14 +205,30 @@ export function VersionDisplay({ onOpenReleaseNotes }: VersionDisplayProps = {})
|
|||||||
}, 2000);
|
}, 2000);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cleanup reconnect interval on unmount
|
// Keep refs in sync with state
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
isUpdatingRef.current = isUpdating;
|
||||||
|
}, [isUpdating]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
isNetworkErrorRef.current = isNetworkError;
|
||||||
|
}, [isNetworkError]);
|
||||||
|
|
||||||
|
// Clear reconnect interval when update completes or component unmounts
|
||||||
|
useEffect(() => {
|
||||||
|
// If we're no longer updating, clear the reconnect interval
|
||||||
|
if (!isUpdating && reconnectIntervalRef.current) {
|
||||||
|
clearInterval(reconnectIntervalRef.current);
|
||||||
|
reconnectIntervalRef.current = null;
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (reconnectIntervalRef.current) {
|
if (reconnectIntervalRef.current) {
|
||||||
clearInterval(reconnectIntervalRef.current);
|
clearInterval(reconnectIntervalRef.current);
|
||||||
|
reconnectIntervalRef.current = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
}, [isUpdating]);
|
||||||
|
|
||||||
const handleUpdate = () => {
|
const handleUpdate = () => {
|
||||||
setIsUpdating(true);
|
setIsUpdating(true);
|
||||||
@@ -200,6 +238,12 @@ export function VersionDisplay({ onOpenReleaseNotes }: VersionDisplayProps = {})
|
|||||||
setShouldSubscribe(false);
|
setShouldSubscribe(false);
|
||||||
setUpdateStartTime(Date.now());
|
setUpdateStartTime(Date.now());
|
||||||
lastLogTimeRef.current = Date.now();
|
lastLogTimeRef.current = Date.now();
|
||||||
|
hasReloadedRef.current = false; // Reset reload flag when starting new update
|
||||||
|
// Clear any existing reconnect interval
|
||||||
|
if (reconnectIntervalRef.current) {
|
||||||
|
clearInterval(reconnectIntervalRef.current);
|
||||||
|
reconnectIntervalRef.current = null;
|
||||||
|
}
|
||||||
executeUpdate.mutate();
|
executeUpdate.mutate();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user