Skip to content

Instantly share code, notes, and snippets.

@daphnesmit
Created January 15, 2024 21:33
Show Gist options
  • Save daphnesmit/da8bd0a33acd8a28a0930027f80930a7 to your computer and use it in GitHub Desktop.
Save daphnesmit/da8bd0a33acd8a28a0930027f80930a7 to your computer and use it in GitHub Desktop.
Create a functional WebSocketConnection instance
import { useCallback, useEffect, useRef, useState } from "react";
import { WebSocketConnection, createWebSocketConnection } from "./ws";
function Example()
const wsRef = useRef<WebSocketConnection | null>(null);
const [data, setData] = useState<{ market: string; volume: string }[]>([]);
const onMessage: WebSocket["onmessage"] = useCallback(
(message: MessageEvent) => {
const data = JSON.parse(message.data);
switch (data.event) {
case "ticker24h":
setData(data.data);
break;
}
},
[]
);
useEffect(() => {
if (!wsRef.current) {
const options = {
onOpen: () => console.log("Connection opened"),
onClose: () => console.log("Connection closed"),
onMessage,
onError: (error: Event) => console.log("Error occurred:", error),
shouldReconnect: true,
reconnectInterval: 3000,
reconnectAttempts: 5,
retryOnError: true,
};
wsRef.current = createWebSocketConnection(
"wss://ws.bitvavo.com/v2",
options
);
wsRef.current.sendMessage(
JSON.stringify({
action: "subscribe",
channels: [{ name: "ticker24h", markets: ['BTC'] }],
}),
{ persistent: true }
);
return () => {
wsRef.current?.flushMessageQueue();
};
}
}, [onMessage]);
return (
<div>
<h1>Websocket data</h1>
<ul>
{data.map((item) => (
<li key={item.market}>
{item.market} - {item.volume}
</li>
))}
</ul>
</div>
);
}
export default Markets;
interface Options {
onOpen?: () => void;
onClose?: () => void;
onMessage?: (message: MessageEvent) => void;
onError?: (error: Event) => void;
shouldReconnect?: boolean;
reconnectInterval?: number;
reconnectAttempts?: number;
retryOnError?: boolean;
}
export interface WebSocketConnection {
sendMessage: (message: string, options?: { persistent: boolean }) => void;
flushMessageQueue: () => void;
}
export const createWebSocketConnection = (
url: string,
options: Options
): WebSocketConnection => {
let ws: WebSocket | null = null;
let messageQueue: string[] = [];
const persistentMessageQueue: string[] = [];
const connect = (): WebSocket | undefined => {
if (!ws || ws.readyState > WebSocket.OPEN) {
ws = new WebSocket(url);
ws.onopen = () => {
options.onOpen?.();
flushMessageQueue();
};
ws.onclose = options.onClose ?? (() => {});
ws.onmessage = options.onMessage ?? (() => {});
ws.onerror = options.onError ?? (() => {});
return ws;
}
return ws;
};
const handleVisibilityChange = () => {
if (document.hidden) {
ws?.close();
} else {
if (!ws || ws.readyState > WebSocket.OPEN) {
connect();
}
}
};
document.addEventListener("visibilitychange", handleVisibilityChange);
const sendMessage = (
message: string,
{ persistent } = { persistent: false }
) => {
messageQueue.push(message);
if (persistent) {
persistentMessageQueue.push(message);
}
if (ws?.readyState === WebSocket.OPEN) {
flushMessageQueue();
}
};
const flushMessageQueue = () => {
if (ws?.readyState === WebSocket.OPEN) {
messageQueue.forEach((message) => ws?.send(message));
messageQueue = [...persistentMessageQueue];
}
};
connect();
return { sendMessage, flushMessageQueue };
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment