You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
128 lines
3.7 KiB
128 lines
3.7 KiB
import {
|
|
useEffect,
|
|
useRef,
|
|
useState,
|
|
} from 'react'
|
|
|
|
interface WebSocketHandlers<T> {
|
|
onClose?: (event: CloseEvent) => void,
|
|
onError?: (event: Event) => void,
|
|
onMessage?: (data: T) => void,
|
|
onOpen?: (event: Event) => void,
|
|
}
|
|
|
|
interface WebSocketOptions<T> {
|
|
allowConnection?: boolean, // Разрешено ли подключение
|
|
autoReconnect?: boolean, // Автоматическое переподключение при ошибке
|
|
handlers?: WebSocketHandlers<T>, // Обработчики событий
|
|
maxReconnectAttempts?: number, // Максимальное количество попыток переподключения
|
|
reconnectInterval?: number, // Интервал между попытками переподключения
|
|
url: string, // Url запроса
|
|
}
|
|
|
|
export const useWebSocket = <T, >({
|
|
allowConnection = true,
|
|
autoReconnect = false,
|
|
handlers = {},
|
|
maxReconnectAttempts = 5,
|
|
reconnectInterval = 5000,
|
|
url,
|
|
}: WebSocketOptions<T>) => {
|
|
const {
|
|
onClose,
|
|
onError,
|
|
onMessage,
|
|
onOpen,
|
|
} = handlers
|
|
|
|
const webSocketRef = useRef<WebSocket | null>(null)
|
|
const timeoutIdRef = useRef<NodeJS.Timeout | null>(null)
|
|
const reconnectAttemptsRef = useRef<number>(0)
|
|
const onCloseRef = useRef(onClose)
|
|
const onErrorRef = useRef(onError)
|
|
const onMessageRef = useRef(onMessage)
|
|
const onOpenRef = useRef(onOpen)
|
|
const [webSocketState, setWebSocketState] = useState(WebSocket.CONNECTING)
|
|
|
|
const sendMessage = (message: Record<string, unknown>) => {
|
|
if (webSocketRef.current?.readyState !== WebSocket.OPEN) return
|
|
|
|
const body = JSON.stringify(message)
|
|
|
|
webSocketRef.current?.send(body)
|
|
}
|
|
|
|
const closeConnection = () => {
|
|
setWebSocketState(WebSocket.CLOSING)
|
|
webSocketRef.current?.close()
|
|
}
|
|
|
|
useEffect(() => {
|
|
onCloseRef.current = onClose
|
|
onErrorRef.current = onError
|
|
onMessageRef.current = onMessage
|
|
onOpenRef.current = onOpen
|
|
}, [onClose, onError, onMessage, onOpen])
|
|
|
|
useEffect(() => {
|
|
const connect = () => {
|
|
if (!allowConnection) return
|
|
|
|
const ws = new WebSocket(url)
|
|
|
|
ws.addEventListener('open', handleOpen)
|
|
ws.addEventListener('message', handleMessage)
|
|
ws.addEventListener('close', handleClose)
|
|
ws.addEventListener('error', handleError)
|
|
|
|
webSocketRef.current = ws
|
|
setWebSocketState(WebSocket.CONNECTING)
|
|
}
|
|
|
|
const handleOpen = (event: Event) => {
|
|
setWebSocketState(WebSocket.OPEN)
|
|
onOpenRef.current?.(event)
|
|
reconnectAttemptsRef.current = 0
|
|
}
|
|
|
|
const handleMessage = (event: MessageEvent) => {
|
|
const data = JSON.parse(event.data)
|
|
|
|
onMessageRef.current?.(data)
|
|
}
|
|
|
|
const handleClose = (event: CloseEvent) => {
|
|
setWebSocketState(WebSocket.CLOSED)
|
|
onCloseRef.current?.(event)
|
|
}
|
|
|
|
const handleError = (event: Event) => {
|
|
setWebSocketState(WebSocket.CLOSED)
|
|
onErrorRef.current?.(event)
|
|
|
|
if (autoReconnect && reconnectAttemptsRef.current < maxReconnectAttempts) {
|
|
timeoutIdRef.current = setTimeout(connect, reconnectInterval)
|
|
reconnectAttemptsRef.current++
|
|
}
|
|
}
|
|
|
|
connect()
|
|
|
|
return () => {
|
|
webSocketRef.current?.removeEventListener('open', handleOpen)
|
|
webSocketRef.current?.removeEventListener('message', handleMessage)
|
|
webSocketRef.current?.removeEventListener('close', handleClose)
|
|
webSocketRef.current?.removeEventListener('error', handleError)
|
|
webSocketRef.current?.close()
|
|
|
|
timeoutIdRef.current && clearTimeout(timeoutIdRef.current)
|
|
}
|
|
}, [url, autoReconnect, reconnectInterval, maxReconnectAttempts, allowConnection])
|
|
|
|
return {
|
|
closeConnection,
|
|
sendMessage,
|
|
webSocketState,
|
|
}
|
|
}
|
|
|
|
|