import { useCallback, useEffect, useRef, useState } from 'react'; export const useWebSocket = (url, onMessage) => { const [isConnected, setIsConnected] = useState(false); const isConnectedRef = useRef(false); const [error, setError] = useState(null); const wsRef = useRef(null); const reconnectTimeoutRef = useRef(null); const reconnectAttempts = useRef(0); useEffect(() => { isConnectedRef.current = isConnected; }, [isConnected]); const connect = useCallback(() => { if (wsRef.current?.readyState === WebSocket.OPEN) return; try { console.log('Connecting to WebSocket:', url); const ws = new WebSocket(url); wsRef.current = ws; ws.onopen = () => { console.log('WebSocket connected successfully'); setIsConnected(true); setError(null); reconnectAttempts.current = 0; }; ws.onmessage = (event) => { try { const data = JSON.parse(event.data); onMessage(data); } catch (err) { console.error('Failed to parse WebSocket message:', err); } }; ws.onerror = (event) => { console.error('WebSocket error:', event); console.error('WebSocket readyState:', ws.readyState); setError('Connection error occurred'); }; ws.onclose = (event) => { console.log('WebSocket closed:', event.code, event.reason, 'Clean:', event.wasClean); setIsConnected(false); // Не переподключаемся если закрыто intentionally или с ошибкой авторизации if (event.code === 1008 || event.code === 1000) { console.log('Connection closed intentionally or auth error, not reconnecting'); return; } // Reconnect with exponential backoff if (reconnectAttempts.current < 5) { const delay = Math.min(1000 * Math.pow(2, reconnectAttempts.current), 30000); console.log(`Reconnecting in ${delay}ms...`); reconnectTimeoutRef.current = setTimeout(() => { reconnectAttempts.current++; connect(); }, delay); } else { setError('Max reconnection attempts reached'); } }; } catch (err) { console.error('Failed to create WebSocket:', err); setError(err.message); } }, [url, onMessage]); const disconnect = useCallback(() => { if (reconnectTimeoutRef.current) { clearTimeout(reconnectTimeoutRef.current); } if (wsRef.current) { wsRef.current.close(1000, 'Intentional disconnect'); wsRef.current = null; } setIsConnected(false); }, []); const sendMessage = useCallback((message) => { if (wsRef.current?.readyState === WebSocket.OPEN) { wsRef.current.send(JSON.stringify(message)); console.log('Message sent:', message); return true; } console.warn('WebSocket is not connected, readyState:', wsRef.current?.readyState); setError('Cannot send message: WebSocket not connected'); return false; }, []); useEffect(() => { connect(); return () => disconnect(); }, [connect, disconnect]); return { isConnectedRef, isConnected, error, sendMessage, disconnect, }; };