Design a real-time analytics dashboard with live-updating D3 charts (à la Domo). Apply RADIO.
Real-time transport choice, render performance with live data, and D3+React integration — your exact domain.
Requirements: many widgets, live updates, large datasets, responsive, accessible, configurable layout. Architecture: a Dashboard grid of independent Widget components; a single data layer subscribes to updates and fans them out via a store (selectors so only affected widgets re-render). Transport: WebSocket for true push / high-frequency; SSE for one-way streams; polling as the simple fallback — pick per update frequency. Data model: normalized cache keyed by widget/metric + last-updated timestamps; throttle incoming ticks. D3+React: let React own the DOM/SVG structure and D3 own the math (scales, axes, layouts) — don't let both mutate the DOM. Optimizations: virtualize/lazy-render off-screen widgets, throttle/batch updates (rAF), memoize scales, downsample dense series, code-split heavy chart bundles, show per-widget loading/error, and keep charts accessible (labels, data tables as fallback). Transport tradeoffs in depth: polling is trivial but wastes requests and adds latency; SSE (EventSource) is a simple one-way server→client stream with built-in auto-reconnect, ideal for tickers; WebSocket is full-duplex for high-frequency or bidirectional needs but you own reconnection/backoff and heartbeats. The performance trap is fan-out — a naïve context holding all widget data re-renders every widget on every tick; instead normalize into a store and subscribe each widget to only its slice (selectors / useSyncExternalStore) so one metric update repaints one chart. Batch and throttle incoming ticks to animation frames (rAF) rather than calling setState per message. D3+React division of labor: D3 computes scales, axes, and layouts (the math) while React renders the resulting SVG/DOM — if both mutate the DOM you get double-render bugs and lost React state.
Dashboards, monitoring, trading/analytics UIs, anything with live data and heavy visualization.
// D3 for the math, React for the DOM. Throttle live updates.
function LineChart({ series, width, height }) {
const x = React.useMemo(
() => d3.scaleTime().domain(d3.extent(series, d => d.t)).range([0, width]),
[series, width]
);
const y = React.useMemo(
() => d3.scaleLinear().domain([0, d3.max(series, d => d.v)]).range([height, 0]),
[series, height]
);
const line = d3.line().x(d => x(d.t)).y(d => y(d.v));
return (
<svg width={width} height={height} role="img" aria-label="Metric over time">
<path d={line(series)} fill="none" stroke="currentColor" />
</svg>
);
}
// Data layer: const ws = new WebSocket(url);
// ws.onmessage = throttle(e => store.applyTick(JSON.parse(e.data)), 250);