scrapeRL / frontend /src /hooks /useWebSocket.ts
NeerajCodz's picture
feat: implement React dashboard with components and hooks
0cfd364
import { useEffect, useRef, useCallback, useState } from 'react';
import type { WebSocketMessage } from '@/types';
type MessageHandler = (message: WebSocketMessage) => void;
interface UseWebSocketOptions {
onMessage?: MessageHandler;
onOpen?: () => void;
onClose?: () => void;
onError?: (error: Event) => void;
reconnectAttempts?: number;
reconnectInterval?: number;
autoConnect?: boolean;
}
interface UseWebSocketReturn {
isConnected: boolean;
isConnecting: boolean;
connect: () => void;
disconnect: () => void;
send: (message: unknown) => void;
lastMessage: WebSocketMessage | null;
}
export function useWebSocket(
url: string = '/ws',
options: UseWebSocketOptions = {}
): UseWebSocketReturn {
const {
onMessage,
onOpen,
onClose,
onError,
reconnectAttempts = 5,
reconnectInterval = 3000,
autoConnect = true,
} = options;
const wsRef = useRef<WebSocket | null>(null);
const reconnectCountRef = useRef(0);
const reconnectTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const [isConnected, setIsConnected] = useState(false);
const [isConnecting, setIsConnecting] = useState(false);
const [lastMessage, setLastMessage] = useState<WebSocketMessage | null>(null);
const getWebSocketUrl = useCallback((): string => {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const host = window.location.host;
return url.startsWith('/') ? `${protocol}//${host}${url}` : url;
}, [url]);
const connect = useCallback(() => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
return;
}
setIsConnecting(true);
try {
const wsUrl = getWebSocketUrl();
wsRef.current = new WebSocket(wsUrl);
wsRef.current.onopen = () => {
setIsConnected(true);
setIsConnecting(false);
reconnectCountRef.current = 0;
onOpen?.();
};
wsRef.current.onclose = () => {
setIsConnected(false);
setIsConnecting(false);
onClose?.();
if (reconnectCountRef.current < reconnectAttempts) {
reconnectTimeoutRef.current = setTimeout(() => {
reconnectCountRef.current++;
connect();
}, reconnectInterval);
}
};
wsRef.current.onerror = (event) => {
setIsConnecting(false);
onError?.(event);
};
wsRef.current.onmessage = (event) => {
try {
const message = JSON.parse(event.data as string) as WebSocketMessage;
setLastMessage(message);
onMessage?.(message);
} catch (error) {
console.error('Failed to parse WebSocket message:', error);
}
};
} catch (error) {
setIsConnecting(false);
console.error('Failed to create WebSocket connection:', error);
}
}, [
getWebSocketUrl,
onMessage,
onOpen,
onClose,
onError,
reconnectAttempts,
reconnectInterval,
]);
const disconnect = useCallback(() => {
if (reconnectTimeoutRef.current) {
clearTimeout(reconnectTimeoutRef.current);
reconnectTimeoutRef.current = null;
}
reconnectCountRef.current = reconnectAttempts;
if (wsRef.current) {
wsRef.current.close();
wsRef.current = null;
}
setIsConnected(false);
}, [reconnectAttempts]);
const send = useCallback((message: unknown) => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(JSON.stringify(message));
} else {
console.warn('WebSocket is not connected');
}
}, []);
useEffect(() => {
if (autoConnect) {
connect();
}
return () => {
disconnect();
};
}, [autoConnect, connect, disconnect]);
return {
isConnected,
isConnecting,
connect,
disconnect,
send,
lastMessage,
};
}
export default useWebSocket;