From cef5c8ae11ce8cef18ae2f963bff866d08bd7746 Mon Sep 17 00:00:00 2001 From: alva-p Date: Sun, 25 Oct 2026 01:31:34 -0300 Subject: [PATCH] Listeners del socket que sobreviven a la recarga MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit socketService.on no hacía nada si el socket todavía no existía. Al abrir o recargar una página, sus efectos corren antes de que el SocketProvider conecte, así que en producción (sin el doble montaje de StrictMode) esos listeners se perdían. Por ejemplo: 'Crear mesa' quedaba deshabilitado al entrar directo a /lobbies, y al recargar en plena partida no se volvía a la mesa. Ahora el servicio guarda los listeners y los engancha al crear el socket. Co-Authored-By: Claude --- src/hooks/useTruco.ts | 5 +---- src/services/socketService.ts | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/hooks/useTruco.ts b/src/hooks/useTruco.ts index dcb16c0..2248dec 100644 --- a/src/hooks/useTruco.ts +++ b/src/hooks/useTruco.ts @@ -1,6 +1,5 @@ import { useCallback, useEffect, useState } from 'react'; import { socketService } from '../services/socketService'; -import { useSocketContext } from '../contexts/SocketContext'; import type { TrucoAction, TrucoEvent, TrucoState } from '../types/truco'; export type Bubble = { ev: TrucoEvent; id: number }; @@ -10,7 +9,6 @@ let seq = 0; // Estado de la mesa de truco. Se monta al tope de la página: el server manda truco:state apenas empieza export function useTruco(lobbyId: string) { - const { isConnected } = useSocketContext(); const [state, setState] = useState(null); const [bubbles, setBubbles] = useState>({}); const [handPoints, setHandPoints] = useState([]); @@ -44,8 +42,7 @@ export function useTruco(lobbyId: string) { }; socketService.on('truco:state', onState); return () => socketService.off('truco:state', onState); - // isConnected: en una recarga el socket se crea después del primer render - }, [lobbyId, isConnected]); + }, [lobbyId]); const act = useCallback((action: TrucoAction) => socketService.trucoAct(lobbyId, action).catch(() => {}), [lobbyId]); diff --git a/src/services/socketService.ts b/src/services/socketService.ts index 0603455..56fcc7a 100644 --- a/src/services/socketService.ts +++ b/src/services/socketService.ts @@ -69,8 +69,13 @@ export interface SocketEvents { 'connect_error': (error: Error) => void; } +type Listener = Parameters[1]; + class SocketService { private socket: Socket | null = null; + // Listeners registrados aunque el socket todavía no exista (al abrir o recargar una página, + // sus efectos corren antes de que el SocketProvider conecte). Se enganchan al crear el socket. + private listeners: [string, Listener][] = []; connect(): Promise { return new Promise((resolve, reject) => { @@ -106,6 +111,7 @@ class SocketService { reconnectionDelay: 1000, auth: { token: getSessionToken() }, }); + for (const [event, cb] of this.listeners) this.socket.on(event, cb); this.socket.on('connect', () => { console.log('✅ Conectado al servidor WebSocket', { socketId: this.socket?.id }); @@ -265,17 +271,13 @@ class SocketService { // Métodos para escuchar eventos on(event: K, callback: SocketEvents[K]): void { - if (this.socket) { - console.debug(`socketService.on -> ${String(event)}`); - this.socket.on(event as string, callback as any); - } + this.listeners.push([event as string, callback as Listener]); + this.socket?.on(event as string, callback as Listener); } off(event: K, callback?: SocketEvents[K]): void { - if (this.socket) { - console.debug(`socketService.off -> ${String(event)}`); - this.socket.off(event as string, callback as any); - } + this.listeners = this.listeners.filter(([e, cb]) => e !== event || (callback && cb !== callback)); + this.socket?.off(event as string, callback as any); } // Método genérico para emit (privado - usar métodos públicos async)