fix: handle SSE reconnections

This commit is contained in:
etiennecollin
2025-09-07 02:14:14 -04:00
parent 562a314fc8
commit b8a7ca808b

View File

@@ -1,15 +1,35 @@
import { useEffect, useRef } from "react";
import { useEffect, useRef, useCallback } from "react";
function getBackoffDelay(attempt: number, base = 1000, max = 30000) {
const jitter = Math.random() * 0.3 + 0.85; // 85115% random factor
return Math.min(base * Math.pow(2, attempt), max) * jitter;
}
export function useServerEvents() {
const eventSourceRef = useRef<EventSource | null>(null);
const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const reconnectAttempts = useRef(0);
const maxReconnectAttempts = 5;
const connect = useCallback(() => {
// Avoid reconnecting if already connecting/open
if (
eventSourceRef.current &&
(eventSourceRef.current.readyState === EventSource.OPEN ||
eventSourceRef.current.readyState === EventSource.CONNECTING)
) {
return;
}
// Close existing connection if any
eventSourceRef.current?.close();
useEffect(() => {
console.log("Setting up SSE connection...");
eventSourceRef.current = new EventSource("/api/events");
eventSourceRef.current.onopen = () => {
console.log("SSE connection opened");
reconnectAttempts.current = 0;
};
eventSourceRef.current.onmessage = (event) => {
@@ -23,6 +43,7 @@ export function useServerEvents() {
window.dispatchEvent(new CustomEvent("vouchersUpdated"));
break;
default:
console.warn("Unknown SSE event type:", data.type);
break;
}
} catch (error) {
@@ -30,14 +51,39 @@ export function useServerEvents() {
}
};
eventSourceRef.current.onerror = (error) => {
console.error("SSE connection error:", error);
};
eventSourceRef.current.onerror = (_error) => {
console.log("SSE connection error, attempting to reconnect...");
return () => {
// Close the current connection
eventSourceRef.current?.close();
// Only attempt to reconnect if we haven't exceeded max attempts
if (reconnectAttempts.current < maxReconnectAttempts) {
reconnectAttempts.current++;
const delay = getBackoffDelay(reconnectAttempts.current);
console.log(
`Reconnecting in ${Math.round(delay)}ms (attempt ${reconnectAttempts.current}/${maxReconnectAttempts})`,
);
reconnectTimeoutRef.current = setTimeout(connect, delay);
} else {
console.error("Max reconnection attempts reached, giving up");
}
};
}, []);
useEffect(() => {
connect();
return () => {
// Clear any pending reconnection attempts
reconnectTimeoutRef.current && clearTimeout(reconnectTimeoutRef.current);
// Close the connection
eventSourceRef.current?.close();
};
}, [connect]);
return eventSourceRef.current;
}