Engine Frame Hooks
The engine React bridge: useEngineClock (mount-once playback driver), useEngineFrame (re-render every animation frame for 60fps motion), useEngineVersion (throttled ~5/s via useSyncExternalStore). Smooth AND cheap live UI.
Reference implementation: this source keeps imports to modules from its home codebase. Read it for the technique, adapt the imports to yours; it is not drop-in compile-ready, on purpose.
"use client";
import { useEffect, useReducer, useSyncExternalStore } from "react";
import { engine } from "../engine/SessionEngine";
/** Re-renders the component on (throttled) engine state changes. */
export function useEngineVersion(): number {
return useSyncExternalStore(engine.subscribe, engine.getVersion, engine.getVersion);
}
/**
* Re-renders the component on EVERY animation frame, for smooth sub-throttle
* motion the 180 ms uiVersion bump is too coarse for: moving car markers, live
* telemetry numerals. The playback clock is driven once by useEngineClock; this
* only re-reads `engine.t` per frame (returned for convenience). Mount it on the
* specific live sub-tree that needs 60 fps, not the whole cockpit.
*/
export function useEngineFrame(active = true): number {
const [, tick] = useReducer((n: number) => (n + 1) | 0, 0);
useEffect(() => {
if (!active) return;
let raf = 0;
const loop = () => {
tick();
raf = requestAnimationFrame(loop);
};
raf = requestAnimationFrame(loop);
return () => cancelAnimationFrame(raf);
}, [active]);
return engine.t;
}
/**
* Drives the playback clock. Mount ONCE at the session root; components then read
* `engine.t` (throttled) or call the hot readers with `performance.now()` for 60fps canvas.
* `frame()` is idempotent within a frame, so extra callers are harmless.
*/
export function useEngineClock(active = true): void {
useEffect(() => {
if (!active) return;
let raf = 0;
const tick = () => {
engine.frame(performance.now());
raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
return () => cancelAnimationFrame(raf);
}, [active]);
}
export { engine };