fix(websocket): use singleton socket to prevent disconnection on re-render

This commit is contained in:
hailin 2026-01-10 01:26:49 -08:00
parent bd65a431aa
commit 0cd667b5c8
1 changed files with 30 additions and 9 deletions

View File

@ -2,6 +2,10 @@ import { useCallback, useEffect, useRef } from 'react';
import { io, Socket } from 'socket.io-client'; import { io, Socket } from 'socket.io-client';
import { useChatStore, Message } from '../stores/chatStore'; import { useChatStore, Message } from '../stores/chatStore';
// Singleton socket instance to prevent multiple connections
let globalSocket: Socket | null = null;
let currentUserId: string | null = null;
export function useChat() { export function useChat() {
const socketRef = useRef<Socket | null>(null); const socketRef = useRef<Socket | null>(null);
const { const {
@ -16,14 +20,32 @@ export function useChat() {
setConnected, setConnected,
} = useChatStore(); } = useChatStore();
// Initialize WebSocket connection // Initialize WebSocket connection (singleton pattern)
useEffect(() => { useEffect(() => {
if (!userId) return; if (!userId) return;
// If socket exists for same user, reuse it
if (globalSocket && currentUserId === userId && globalSocket.connected) {
socketRef.current = globalSocket;
setConnected(true);
return;
}
// If socket exists for different user or disconnected, clean up
if (globalSocket) {
globalSocket.disconnect();
globalSocket = null;
}
currentUserId = userId;
const socket = io('/ws/conversation', { const socket = io('/ws/conversation', {
path: '/ws/conversation/socket.io', path: '/ws/conversation/socket.io',
query: { userId }, query: { userId },
transports: ['websocket'], transports: ['websocket'],
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1000,
}); });
socket.on('connect', () => { socket.on('connect', () => {
@ -77,7 +99,6 @@ export function useChat() {
socket.on('tool_result', (data) => { socket.on('tool_result', (data) => {
console.log('Tool result:', data); console.log('Tool result:', data);
// Could update message with tool result
}); });
socket.on('error', (error) => { socket.on('error', (error) => {
@ -85,11 +106,10 @@ export function useChat() {
setStreaming(false); setStreaming(false);
}); });
globalSocket = socket;
socketRef.current = socket; socketRef.current = socket;
return () => { // Don't disconnect on cleanup - keep singleton alive
socket.disconnect();
};
}, [userId]); }, [userId]);
// Send message // Send message
@ -114,15 +134,16 @@ export function useChat() {
}; };
addMessage(conversationId, userMessage); addMessage(conversationId, userMessage);
// Send via WebSocket // Send via WebSocket (use globalSocket for reliability)
if (socketRef.current?.connected) { const socket = globalSocket || socketRef.current;
if (socket?.connected) {
console.log('Sending message via WebSocket:', { conversationId, content: content.trim() }); console.log('Sending message via WebSocket:', { conversationId, content: content.trim() });
socketRef.current.emit('message', { socket.emit('message', {
conversationId, conversationId,
content: content.trim(), content: content.trim(),
}); });
} else { } else {
console.error('WebSocket not connected, cannot send message'); console.error('WebSocket not connected, cannot send message. Socket state:', socket?.connected);
} }
}, },
[userId, currentConversationId, addMessage], [userId, currentConversationId, addMessage],