import { useCallback, useEffect, useRef, useState } from 'react'; export type WsStatus = 'connecting' | 'open' | 'closed' | 'error'; interface UseWebSocketOptions { url: string | null; enabled?: boolean; onMessage?: (data: string) => void; reconnect?: boolean; reconnectIntervalMs?: number; } export function useWebSocket({ url, enabled = true, onMessage, reconnect = true, reconnectIntervalMs = 3000, }: UseWebSocketOptions) { const [status, setStatus] = useState('closed'); const wsRef = useRef(null); const onMessageRef = useRef(onMessage); onMessageRef.current = onMessage; const send = useCallback((data: string) => { if (wsRef.current?.readyState === WebSocket.OPEN) { wsRef.current.send(data); } }, []); useEffect(() => { if (!enabled || !url) { setStatus('closed'); return; } let disposed = false; let reconnectTimer: ReturnType | undefined; const connect = () => { if (disposed) return; setStatus('connecting'); const ws = new WebSocket(url); wsRef.current = ws; ws.onopen = () => { if (!disposed) setStatus('open'); }; ws.onmessage = (event) => { onMessageRef.current?.(String(event.data)); }; ws.onerror = () => { if (!disposed) setStatus('error'); }; ws.onclose = () => { if (!disposed) { setStatus('closed'); if (reconnect) { reconnectTimer = setTimeout(connect, reconnectIntervalMs); } } }; }; connect(); return () => { disposed = true; if (reconnectTimer) clearTimeout(reconnectTimer); wsRef.current?.close(); wsRef.current = null; }; }, [url, enabled, reconnect, reconnectIntervalMs]); return { status, send }; }