brackt/app/hooks/useDraftSocket.ts

157 lines
5.7 KiB
TypeScript
Raw Normal View History

import { useCallback, useEffect, useRef, useState } from "react";
import { io } from "socket.io-client";
interface UseDraftSocketReturn {
isConnected: boolean;
connectionError: string | null;
isReconnecting: boolean;
reconnectCount: number;
/** Increments each time a new socket instance is created. Include in
* useEffect deps so handler effects re-register on socket recreation. */
socketVersion: number;
on: (event: string, callback: (...args: any[]) => void) => void;
off: (event: string, callback?: (...args: any[]) => void) => void;
emit: (event: string, ...args: any[]) => void;
}
export function useDraftSocket(seasonId: string, teamId?: string): UseDraftSocketReturn {
const socketRef = useRef<ReturnType<typeof io> | null>(null);
const hasConnectedOnce = useRef(false);
const [isConnected, setIsConnected] = useState(false);
const [connectionError, setConnectionError] = useState<string | null>(null);
const [isReconnecting, setIsReconnecting] = useState(false);
const [reconnectCount, setReconnectCount] = useState(0);
const [socketVersion, setSocketVersion] = useState(0);
useEffect(() => {
// Reset per-socket state so a new seasonId/teamId doesn't inherit the previous
// socket's connect history and falsely treat its first connect as a reconnect.
hasConnectedOnce.current = false;
const socket = io({
path: "/socket.io",
transports: ["websocket", "polling"],
Fix draft state not updating when returning from backgrounded mobile app (#39) * Fix draft state not updating when returning from backgrounded mobile app Mobile browsers suspend JavaScript and silently drop WebSocket connections when the user switches to another app, without firing "offline"/"online" events. Add a visibilitychange handler so that when the user returns: - If the socket is disconnected, reconnect immediately (skipping backoff) - If the socket appears connected but JS was suspended, rejoin the draft room and trigger a loader revalidation to catch any missed picks/state https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Simplify reconnect handlers and fix connect_error overlay flicker - Merge handleOnline and the shared branch of handleVisibilityChange into a single handleReturn function. visibilitychange is now a thin guard that calls it only on show. Both events share the same logic: reconnect if socket dropped, or restore UI state + rejoin room + revalidate if the socket survived. - Remove setIsReconnecting(false) from connect_error: reconnect_attempt fires immediately after and resets it to true anyway, causing the "Reconnecting" overlay to flicker off and back on during every retry cycle. https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Fix four issues from useDraftSocket code review - Manager listener leak: add socket.io.off() for reconnect_attempt and reconnect_failed in cleanup — socket.disconnect() only tears down the socket, not the Manager listeners, causing them to accumulate on re-mounts. - reconnect_failed dead code: add reconnectionAttempts: 10 to io() config so the handler is actually reachable after exhausting retries. - connectionError flicker: remove setConnectionError from connect_error — reconnect_attempt fires immediately after and clears it anyway, causing the error overlay to flash on every retry cycle. Error now only appears via reconnect_failed once all attempts are exhausted. connect_error instead ensures setIsReconnecting(true) so the reconnecting overlay shows instead of the initial "Connecting to Draft" spinner. - Add comment to on/off/emit noting they are no-ops if called before the effect runs (socketRef.current === null). https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-25 09:36:52 -08:00
reconnectionAttempts: 10,
});
socketRef.current = socket;
setSocketVersion((v) => v + 1);
socket.on("connect", () => {
console.log("Connected to Socket.IO:", socket.id);
const isReconnect = hasConnectedOnce.current;
hasConnectedOnce.current = true;
setIsConnected(true);
setConnectionError(null);
setIsReconnecting(false);
socket.emit("join-draft", seasonId, teamId);
if (isReconnect) {
setReconnectCount((c) => c + 1);
}
});
socket.on("disconnect", (reason) => {
console.log("Disconnected from Socket.IO:", reason);
setIsConnected(false);
if (reason === "io server disconnect") {
setConnectionError("Server disconnected. Please refresh the page.");
} else {
setIsReconnecting(true);
}
});
socket.on("connect_error", (error) => {
console.error("Socket.IO connection error:", error);
Fix draft state not updating when returning from backgrounded mobile app (#39) * Fix draft state not updating when returning from backgrounded mobile app Mobile browsers suspend JavaScript and silently drop WebSocket connections when the user switches to another app, without firing "offline"/"online" events. Add a visibilitychange handler so that when the user returns: - If the socket is disconnected, reconnect immediately (skipping backoff) - If the socket appears connected but JS was suspended, rejoin the draft room and trigger a loader revalidation to catch any missed picks/state https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Simplify reconnect handlers and fix connect_error overlay flicker - Merge handleOnline and the shared branch of handleVisibilityChange into a single handleReturn function. visibilitychange is now a thin guard that calls it only on show. Both events share the same logic: reconnect if socket dropped, or restore UI state + rejoin room + revalidate if the socket survived. - Remove setIsReconnecting(false) from connect_error: reconnect_attempt fires immediately after and resets it to true anyway, causing the "Reconnecting" overlay to flicker off and back on during every retry cycle. https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Fix four issues from useDraftSocket code review - Manager listener leak: add socket.io.off() for reconnect_attempt and reconnect_failed in cleanup — socket.disconnect() only tears down the socket, not the Manager listeners, causing them to accumulate on re-mounts. - reconnect_failed dead code: add reconnectionAttempts: 10 to io() config so the handler is actually reachable after exhausting retries. - connectionError flicker: remove setConnectionError from connect_error — reconnect_attempt fires immediately after and clears it anyway, causing the error overlay to flash on every retry cycle. Error now only appears via reconnect_failed once all attempts are exhausted. connect_error instead ensures setIsReconnecting(true) so the reconnecting overlay shows instead of the initial "Connecting to Draft" spinner. - Add comment to on/off/emit noting they are no-ops if called before the effect runs (socketRef.current === null). https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-25 09:36:52 -08:00
// Don't set connectionError here — reconnect_attempt fires immediately after
// and would clear it again, causing the error overlay to flicker on every
// retry. Only show a hard error once all attempts are exhausted (reconnect_failed).
// Ensure the "reconnecting" overlay is visible rather than the initial spinner.
setIsReconnecting(true);
});
socket.io.on("reconnect_attempt", () => {
console.log("Attempting to reconnect...");
setIsReconnecting(true);
setConnectionError(null);
});
socket.io.on("reconnect_failed", () => {
console.error("Reconnection failed");
setConnectionError("Failed to reconnect. Please refresh the page.");
setIsReconnecting(false);
});
const handleOffline = () => {
// Mark as reconnecting immediately — the OS fires this before Socket.IO's
// heartbeat would detect the drop (~15s later). Note: "offline" can fire
Fix draft state not updating when returning from backgrounded mobile app (#39) * Fix draft state not updating when returning from backgrounded mobile app Mobile browsers suspend JavaScript and silently drop WebSocket connections when the user switches to another app, without firing "offline"/"online" events. Add a visibilitychange handler so that when the user returns: - If the socket is disconnected, reconnect immediately (skipping backoff) - If the socket appears connected but JS was suspended, rejoin the draft room and trigger a loader revalidation to catch any missed picks/state https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Simplify reconnect handlers and fix connect_error overlay flicker - Merge handleOnline and the shared branch of handleVisibilityChange into a single handleReturn function. visibilitychange is now a thin guard that calls it only on show. Both events share the same logic: reconnect if socket dropped, or restore UI state + rejoin room + revalidate if the socket survived. - Remove setIsReconnecting(false) from connect_error: reconnect_attempt fires immediately after and resets it to true anyway, causing the "Reconnecting" overlay to flicker off and back on during every retry cycle. https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Fix four issues from useDraftSocket code review - Manager listener leak: add socket.io.off() for reconnect_attempt and reconnect_failed in cleanup — socket.disconnect() only tears down the socket, not the Manager listeners, causing them to accumulate on re-mounts. - reconnect_failed dead code: add reconnectionAttempts: 10 to io() config so the handler is actually reachable after exhausting retries. - connectionError flicker: remove setConnectionError from connect_error — reconnect_attempt fires immediately after and clears it anyway, causing the error overlay to flash on every retry cycle. Error now only appears via reconnect_failed once all attempts are exhausted. connect_error instead ensures setIsReconnecting(true) so the reconnecting overlay shows instead of the initial "Connecting to Draft" spinner. - Add comment to on/off/emit noting they are no-ops if called before the effect runs (socketRef.current === null). https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-25 09:36:52 -08:00
// spuriously on mobile; handleReturn corrects the state if the socket is
// still alive when the network returns.
setIsConnected(false);
setIsReconnecting(true);
};
Fix draft state not updating when returning from backgrounded mobile app (#39) * Fix draft state not updating when returning from backgrounded mobile app Mobile browsers suspend JavaScript and silently drop WebSocket connections when the user switches to another app, without firing "offline"/"online" events. Add a visibilitychange handler so that when the user returns: - If the socket is disconnected, reconnect immediately (skipping backoff) - If the socket appears connected but JS was suspended, rejoin the draft room and trigger a loader revalidation to catch any missed picks/state https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Simplify reconnect handlers and fix connect_error overlay flicker - Merge handleOnline and the shared branch of handleVisibilityChange into a single handleReturn function. visibilitychange is now a thin guard that calls it only on show. Both events share the same logic: reconnect if socket dropped, or restore UI state + rejoin room + revalidate if the socket survived. - Remove setIsReconnecting(false) from connect_error: reconnect_attempt fires immediately after and resets it to true anyway, causing the "Reconnecting" overlay to flicker off and back on during every retry cycle. https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Fix four issues from useDraftSocket code review - Manager listener leak: add socket.io.off() for reconnect_attempt and reconnect_failed in cleanup — socket.disconnect() only tears down the socket, not the Manager listeners, causing them to accumulate on re-mounts. - reconnect_failed dead code: add reconnectionAttempts: 10 to io() config so the handler is actually reachable after exhausting retries. - connectionError flicker: remove setConnectionError from connect_error — reconnect_attempt fires immediately after and clears it anyway, causing the error overlay to flash on every retry cycle. Error now only appears via reconnect_failed once all attempts are exhausted. connect_error instead ensures setIsReconnecting(true) so the reconnecting overlay shows instead of the initial "Connecting to Draft" spinner. - Add comment to on/off/emit noting they are no-ops if called before the effect runs (socketRef.current === null). https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-25 09:36:52 -08:00
// Unified handler for both "network returned" (online event) and
// "browser tab/app regained focus" (visibilitychange). Two cases:
// - Socket dropped: reconnect immediately, bypassing backoff.
// - Socket alive: restore UI state and rejoin the draft room so the page
// revalidates and picks up any events missed while the client was away.
const handleReturn = () => {
if (!socketRef.current?.connected) {
socketRef.current?.connect();
} else {
setIsConnected(true);
setIsReconnecting(false);
Fix draft state not updating when returning from backgrounded mobile app (#39) * Fix draft state not updating when returning from backgrounded mobile app Mobile browsers suspend JavaScript and silently drop WebSocket connections when the user switches to another app, without firing "offline"/"online" events. Add a visibilitychange handler so that when the user returns: - If the socket is disconnected, reconnect immediately (skipping backoff) - If the socket appears connected but JS was suspended, rejoin the draft room and trigger a loader revalidation to catch any missed picks/state https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Simplify reconnect handlers and fix connect_error overlay flicker - Merge handleOnline and the shared branch of handleVisibilityChange into a single handleReturn function. visibilitychange is now a thin guard that calls it only on show. Both events share the same logic: reconnect if socket dropped, or restore UI state + rejoin room + revalidate if the socket survived. - Remove setIsReconnecting(false) from connect_error: reconnect_attempt fires immediately after and resets it to true anyway, causing the "Reconnecting" overlay to flicker off and back on during every retry cycle. https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Fix four issues from useDraftSocket code review - Manager listener leak: add socket.io.off() for reconnect_attempt and reconnect_failed in cleanup — socket.disconnect() only tears down the socket, not the Manager listeners, causing them to accumulate on re-mounts. - reconnect_failed dead code: add reconnectionAttempts: 10 to io() config so the handler is actually reachable after exhausting retries. - connectionError flicker: remove setConnectionError from connect_error — reconnect_attempt fires immediately after and clears it anyway, causing the error overlay to flash on every retry cycle. Error now only appears via reconnect_failed once all attempts are exhausted. connect_error instead ensures setIsReconnecting(true) so the reconnecting overlay shows instead of the initial "Connecting to Draft" spinner. - Add comment to on/off/emit noting they are no-ops if called before the effect runs (socketRef.current === null). https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-25 09:36:52 -08:00
socketRef.current.emit("join-draft", seasonId, teamId);
setReconnectCount((c) => c + 1);
}
};
Fix draft state not updating when returning from backgrounded mobile app (#39) * Fix draft state not updating when returning from backgrounded mobile app Mobile browsers suspend JavaScript and silently drop WebSocket connections when the user switches to another app, without firing "offline"/"online" events. Add a visibilitychange handler so that when the user returns: - If the socket is disconnected, reconnect immediately (skipping backoff) - If the socket appears connected but JS was suspended, rejoin the draft room and trigger a loader revalidation to catch any missed picks/state https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Simplify reconnect handlers and fix connect_error overlay flicker - Merge handleOnline and the shared branch of handleVisibilityChange into a single handleReturn function. visibilitychange is now a thin guard that calls it only on show. Both events share the same logic: reconnect if socket dropped, or restore UI state + rejoin room + revalidate if the socket survived. - Remove setIsReconnecting(false) from connect_error: reconnect_attempt fires immediately after and resets it to true anyway, causing the "Reconnecting" overlay to flicker off and back on during every retry cycle. https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Fix four issues from useDraftSocket code review - Manager listener leak: add socket.io.off() for reconnect_attempt and reconnect_failed in cleanup — socket.disconnect() only tears down the socket, not the Manager listeners, causing them to accumulate on re-mounts. - reconnect_failed dead code: add reconnectionAttempts: 10 to io() config so the handler is actually reachable after exhausting retries. - connectionError flicker: remove setConnectionError from connect_error — reconnect_attempt fires immediately after and clears it anyway, causing the error overlay to flash on every retry cycle. Error now only appears via reconnect_failed once all attempts are exhausted. connect_error instead ensures setIsReconnecting(true) so the reconnecting overlay shows instead of the initial "Connecting to Draft" spinner. - Add comment to on/off/emit noting they are no-ops if called before the effect runs (socketRef.current === null). https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-25 09:36:52 -08:00
// visibilitychange fires on both hide and show — only act on show.
const handleVisibilityChange = () => {
if (document.visibilityState === "visible") handleReturn();
};
window.addEventListener("offline", handleOffline);
Fix draft state not updating when returning from backgrounded mobile app (#39) * Fix draft state not updating when returning from backgrounded mobile app Mobile browsers suspend JavaScript and silently drop WebSocket connections when the user switches to another app, without firing "offline"/"online" events. Add a visibilitychange handler so that when the user returns: - If the socket is disconnected, reconnect immediately (skipping backoff) - If the socket appears connected but JS was suspended, rejoin the draft room and trigger a loader revalidation to catch any missed picks/state https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Simplify reconnect handlers and fix connect_error overlay flicker - Merge handleOnline and the shared branch of handleVisibilityChange into a single handleReturn function. visibilitychange is now a thin guard that calls it only on show. Both events share the same logic: reconnect if socket dropped, or restore UI state + rejoin room + revalidate if the socket survived. - Remove setIsReconnecting(false) from connect_error: reconnect_attempt fires immediately after and resets it to true anyway, causing the "Reconnecting" overlay to flicker off and back on during every retry cycle. https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Fix four issues from useDraftSocket code review - Manager listener leak: add socket.io.off() for reconnect_attempt and reconnect_failed in cleanup — socket.disconnect() only tears down the socket, not the Manager listeners, causing them to accumulate on re-mounts. - reconnect_failed dead code: add reconnectionAttempts: 10 to io() config so the handler is actually reachable after exhausting retries. - connectionError flicker: remove setConnectionError from connect_error — reconnect_attempt fires immediately after and clears it anyway, causing the error overlay to flash on every retry cycle. Error now only appears via reconnect_failed once all attempts are exhausted. connect_error instead ensures setIsReconnecting(true) so the reconnecting overlay shows instead of the initial "Connecting to Draft" spinner. - Add comment to on/off/emit noting they are no-ops if called before the effect runs (socketRef.current === null). https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-25 09:36:52 -08:00
window.addEventListener("online", handleReturn);
document.addEventListener("visibilitychange", handleVisibilityChange);
return () => {
Fix draft state not updating when returning from backgrounded mobile app (#39) * Fix draft state not updating when returning from backgrounded mobile app Mobile browsers suspend JavaScript and silently drop WebSocket connections when the user switches to another app, without firing "offline"/"online" events. Add a visibilitychange handler so that when the user returns: - If the socket is disconnected, reconnect immediately (skipping backoff) - If the socket appears connected but JS was suspended, rejoin the draft room and trigger a loader revalidation to catch any missed picks/state https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Simplify reconnect handlers and fix connect_error overlay flicker - Merge handleOnline and the shared branch of handleVisibilityChange into a single handleReturn function. visibilitychange is now a thin guard that calls it only on show. Both events share the same logic: reconnect if socket dropped, or restore UI state + rejoin room + revalidate if the socket survived. - Remove setIsReconnecting(false) from connect_error: reconnect_attempt fires immediately after and resets it to true anyway, causing the "Reconnecting" overlay to flicker off and back on during every retry cycle. https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Fix four issues from useDraftSocket code review - Manager listener leak: add socket.io.off() for reconnect_attempt and reconnect_failed in cleanup — socket.disconnect() only tears down the socket, not the Manager listeners, causing them to accumulate on re-mounts. - reconnect_failed dead code: add reconnectionAttempts: 10 to io() config so the handler is actually reachable after exhausting retries. - connectionError flicker: remove setConnectionError from connect_error — reconnect_attempt fires immediately after and clears it anyway, causing the error overlay to flash on every retry cycle. Error now only appears via reconnect_failed once all attempts are exhausted. connect_error instead ensures setIsReconnecting(true) so the reconnecting overlay shows instead of the initial "Connecting to Draft" spinner. - Add comment to on/off/emit noting they are no-ops if called before the effect runs (socketRef.current === null). https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-25 09:36:52 -08:00
socket.io.off("reconnect_attempt");
socket.io.off("reconnect_failed");
window.removeEventListener("offline", handleOffline);
Fix draft state not updating when returning from backgrounded mobile app (#39) * Fix draft state not updating when returning from backgrounded mobile app Mobile browsers suspend JavaScript and silently drop WebSocket connections when the user switches to another app, without firing "offline"/"online" events. Add a visibilitychange handler so that when the user returns: - If the socket is disconnected, reconnect immediately (skipping backoff) - If the socket appears connected but JS was suspended, rejoin the draft room and trigger a loader revalidation to catch any missed picks/state https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Simplify reconnect handlers and fix connect_error overlay flicker - Merge handleOnline and the shared branch of handleVisibilityChange into a single handleReturn function. visibilitychange is now a thin guard that calls it only on show. Both events share the same logic: reconnect if socket dropped, or restore UI state + rejoin room + revalidate if the socket survived. - Remove setIsReconnecting(false) from connect_error: reconnect_attempt fires immediately after and resets it to true anyway, causing the "Reconnecting" overlay to flicker off and back on during every retry cycle. https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Fix four issues from useDraftSocket code review - Manager listener leak: add socket.io.off() for reconnect_attempt and reconnect_failed in cleanup — socket.disconnect() only tears down the socket, not the Manager listeners, causing them to accumulate on re-mounts. - reconnect_failed dead code: add reconnectionAttempts: 10 to io() config so the handler is actually reachable after exhausting retries. - connectionError flicker: remove setConnectionError from connect_error — reconnect_attempt fires immediately after and clears it anyway, causing the error overlay to flash on every retry cycle. Error now only appears via reconnect_failed once all attempts are exhausted. connect_error instead ensures setIsReconnecting(true) so the reconnecting overlay shows instead of the initial "Connecting to Draft" spinner. - Add comment to on/off/emit noting they are no-ops if called before the effect runs (socketRef.current === null). https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-25 09:36:52 -08:00
window.removeEventListener("online", handleReturn);
document.removeEventListener("visibilitychange", handleVisibilityChange);
console.log("Leaving draft room:", seasonId);
socket.emit("leave-draft", seasonId);
socket.disconnect();
};
}, [seasonId, teamId]);
Fix draft state not updating when returning from backgrounded mobile app (#39) * Fix draft state not updating when returning from backgrounded mobile app Mobile browsers suspend JavaScript and silently drop WebSocket connections when the user switches to another app, without firing "offline"/"online" events. Add a visibilitychange handler so that when the user returns: - If the socket is disconnected, reconnect immediately (skipping backoff) - If the socket appears connected but JS was suspended, rejoin the draft room and trigger a loader revalidation to catch any missed picks/state https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Simplify reconnect handlers and fix connect_error overlay flicker - Merge handleOnline and the shared branch of handleVisibilityChange into a single handleReturn function. visibilitychange is now a thin guard that calls it only on show. Both events share the same logic: reconnect if socket dropped, or restore UI state + rejoin room + revalidate if the socket survived. - Remove setIsReconnecting(false) from connect_error: reconnect_attempt fires immediately after and resets it to true anyway, causing the "Reconnecting" overlay to flicker off and back on during every retry cycle. https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt * Fix four issues from useDraftSocket code review - Manager listener leak: add socket.io.off() for reconnect_attempt and reconnect_failed in cleanup — socket.disconnect() only tears down the socket, not the Manager listeners, causing them to accumulate on re-mounts. - reconnect_failed dead code: add reconnectionAttempts: 10 to io() config so the handler is actually reachable after exhausting retries. - connectionError flicker: remove setConnectionError from connect_error — reconnect_attempt fires immediately after and clears it anyway, causing the error overlay to flash on every retry cycle. Error now only appears via reconnect_failed once all attempts are exhausted. connect_error instead ensures setIsReconnecting(true) so the reconnecting overlay shows instead of the initial "Connecting to Draft" spinner. - Add comment to on/off/emit noting they are no-ops if called before the effect runs (socketRef.current === null). https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-25 09:36:52 -08:00
// These read socketRef.current at call time, so calls made before the effect
// has run (socketRef.current === null) are silently no-ops. Consumers should
// only call them inside their own useEffect, not during render or synchronously
// after mount.
const on = useCallback((event: string, callback: (...args: any[]) => void) => {
socketRef.current?.on(event, callback);
}, []);
const off = useCallback((event: string, callback?: (...args: any[]) => void) => {
socketRef.current?.off(event, callback);
}, []);
const emit = useCallback((event: string, ...args: any[]) => {
socketRef.current?.emit(event, ...args);
}, []);
return {
isConnected,
connectionError,
isReconnecting,
reconnectCount,
socketVersion,
on,
off,
emit,
};
}