valhalla / frontend /src /hooks /useSimState.js
Dontcryx_07
final
d660fa9
Raw
History Blame Contribute Delete
1.72 kB
/**
* useSimState — subscribes to the simulation WebSocket.
*
* Connects to /ws/sim, stores the latest JSON snapshot, and auto-reconnects
* every 3 seconds on drop so the dashboard survives brief network hiccups.
*
* Architecture: the single data source for the whole dashboard; consumed
* by App.jsx and distributed to panels via props.
*
* Design: last-write-wins snapshot updates keep rendering stateless.
*/
import { useEffect, useRef, useState, useCallback } from "react";
import { wsUrl } from "../utils/api";
export default function useSimState() {
const [snapshot, setSnapshot] = useState(null);
const wsRef = useRef(null);
const reconnectTimer = useRef(null);
const disposedRef = useRef(false);
const connect = useCallback(() => {
if (disposedRef.current) return;
clearTimeout(reconnectTimer.current);
const url = wsUrl("/ws/sim");
const ws = new WebSocket(url);
ws.onmessage = (e) => {
try {
const data = JSON.parse(e.data);
setSnapshot(data);
} catch { /* ignore parse errors */ }
};
ws.onclose = () => {
// that case creates an orphan connection after the view has unmounted.
if (disposedRef.current || wsRef.current !== ws) return;
reconnectTimer.current = setTimeout(connect, 3000);
};
ws.onerror = () => ws.close();
wsRef.current = ws;
}, []);
useEffect(() => {
disposedRef.current = false;
connect();
return () => {
clearTimeout(reconnectTimer.current);
disposedRef.current = true;
const ws = wsRef.current;
wsRef.current = null;
if (ws) {
ws.onclose = null;
ws.close();
}
};
}, [connect]);
return snapshot;
}