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.
 
 
 
 
spa_instat_tv/src/hooks/useWebSocket.tsx

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,
}
}