108 lines
2.9 KiB
JavaScript
108 lines
2.9 KiB
JavaScript
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,
|
||
};
|
||
};
|