145 lines
4.2 KiB
TypeScript
145 lines
4.2 KiB
TypeScript
|
import React from "react";
|
||
|
|
||
|
export type UseWebsocketHookParams = {
|
||
|
debounce?: number;
|
||
|
url: string;
|
||
|
};
|
||
|
|
||
|
let reconnectInterval: any;
|
||
|
let msgInterval: any;
|
||
|
let sendInterval: any;
|
||
|
|
||
|
let tries = 0;
|
||
|
|
||
|
export const WebSocketEventNames = ["wsDataEvent", "wsMessageEvent"] as const;
|
||
|
|
||
|
/**
|
||
|
* # Use Websocket Hook
|
||
|
* @event wsDataEvent Listen for event named `wsDataEvent` on `window` to receive Data events
|
||
|
* @event wsMessageEvent Listen for event named `wsMessageEvent` on `window` to receive Message events
|
||
|
*
|
||
|
* @example window.addEventLiatener("wsDataEvent", (e)=>{
|
||
|
* console.log(e.detail.data) // type object
|
||
|
* })
|
||
|
* @example window.addEventLiatener("wsMessageEvent", (e)=>{
|
||
|
* console.log(e.detail.message) // type string
|
||
|
* })
|
||
|
*/
|
||
|
export default function useWebSocket<T>({
|
||
|
url,
|
||
|
debounce,
|
||
|
}: UseWebsocketHookParams) {
|
||
|
const DEBOUNCE = debounce || 200;
|
||
|
|
||
|
const [socket, setSocket] = React.useState<WebSocket | undefined>(
|
||
|
undefined
|
||
|
);
|
||
|
|
||
|
const messageQueueRef = React.useRef<string[]>([]);
|
||
|
const sendMessageQueueRef = React.useRef<string[]>([]);
|
||
|
|
||
|
// const [message, setMessage] = React.useState<string>("");
|
||
|
// const [data, setData] = React.useState<T | null>(null);
|
||
|
const [refresh, setRefresh] = React.useState(0);
|
||
|
|
||
|
const dispatchCustomEvent = React.useCallback(
|
||
|
(evtName: (typeof WebSocketEventNames)[number], value: string | T) => {
|
||
|
const event = new CustomEvent(evtName, {
|
||
|
detail: {
|
||
|
data: value,
|
||
|
message: value,
|
||
|
},
|
||
|
});
|
||
|
window.dispatchEvent(event);
|
||
|
},
|
||
|
[]
|
||
|
);
|
||
|
|
||
|
React.useEffect(() => {
|
||
|
const wsURL = url;
|
||
|
if (!wsURL) return;
|
||
|
|
||
|
const ws = new WebSocket(wsURL);
|
||
|
|
||
|
ws.onopen = (ev) => {
|
||
|
window.clearInterval(reconnectInterval);
|
||
|
setSocket(ws);
|
||
|
tries = 0;
|
||
|
};
|
||
|
|
||
|
ws.onmessage = (ev) => {
|
||
|
window.clearInterval(msgInterval);
|
||
|
messageQueueRef.current.push(ev.data);
|
||
|
msgInterval = setInterval(handleReceivedMessageQueue, DEBOUNCE);
|
||
|
};
|
||
|
|
||
|
ws.onclose = (ev) => {
|
||
|
console.log("Websocket closed ... Attempting to reconnect ...");
|
||
|
reconnectInterval = setInterval(() => {
|
||
|
if (tries >= 3) {
|
||
|
return window.clearInterval(reconnectInterval);
|
||
|
}
|
||
|
console.log("Attempting to reconnect ...");
|
||
|
setRefresh(refresh + 1);
|
||
|
tries++;
|
||
|
}, 1000);
|
||
|
};
|
||
|
|
||
|
return function () {
|
||
|
window.clearInterval(reconnectInterval);
|
||
|
};
|
||
|
}, [refresh]);
|
||
|
|
||
|
/**
|
||
|
* Received Message Queue Handler
|
||
|
*/
|
||
|
const handleReceivedMessageQueue = React.useCallback(() => {
|
||
|
if (messageQueueRef.current.length > 0) {
|
||
|
const newMessage = messageQueueRef.current.shift();
|
||
|
if (!newMessage) return;
|
||
|
try {
|
||
|
const jsonData = JSON.parse(newMessage);
|
||
|
// setData(jsonData);
|
||
|
|
||
|
dispatchCustomEvent("wsMessageEvent", newMessage);
|
||
|
dispatchCustomEvent("wsDataEvent", jsonData);
|
||
|
} catch (error) {
|
||
|
console.log("Unable to parse string. Returning string.");
|
||
|
}
|
||
|
} else {
|
||
|
window.clearInterval(msgInterval);
|
||
|
}
|
||
|
}, []);
|
||
|
|
||
|
/**
|
||
|
* Send Message Queue Handler
|
||
|
*/
|
||
|
const handleSendMessageQueue = React.useCallback(() => {
|
||
|
if (sendMessageQueueRef.current.length > 0) {
|
||
|
const newMessage = sendMessageQueueRef.current.shift();
|
||
|
if (!newMessage) return;
|
||
|
socket?.send(newMessage);
|
||
|
} else {
|
||
|
window.clearInterval(sendInterval);
|
||
|
}
|
||
|
}, [socket]);
|
||
|
|
||
|
/**
|
||
|
* # Send Data Function
|
||
|
*/
|
||
|
const sendData = React.useCallback(
|
||
|
(data: T) => {
|
||
|
try {
|
||
|
window.clearInterval(sendInterval);
|
||
|
sendMessageQueueRef.current.push(JSON.stringify(data));
|
||
|
sendInterval = setInterval(handleSendMessageQueue, DEBOUNCE);
|
||
|
} catch (error: any) {
|
||
|
console.log("Error Sending socket message", error.message);
|
||
|
}
|
||
|
},
|
||
|
[socket]
|
||
|
);
|
||
|
|
||
|
return { socket, sendData };
|
||
|
}
|