esc

Type to search...

Trading ticks

Market data is the textbook case for receive-side backpressure: 50+ messages per second, every one identically shaped, with the only thing that matters being the latest value. Re-rendering on every tick is correct semantics and ruinous performance. Batching collapses the burst into one render per flush window with the same latest-value guarantee.

Two panels driven by the same wire stream. Left: useSocketEvent, one render per tick. Right: useSocketEventBatch at flushMs: 100, ~10 renders/sec for the same data.

Live demo

Trading ticks

idle

The market feed pushes 50 quotes per second. The left panel re-renders on every tick — fine for one badge, ruinous for a real ladder. The right panel buffers and flushes every 100ms; same data, ~6× fewer renders. Watch Last batch on the right to see batching happen — each flush surfaces the size of the buffer that just drained.

Per-event
useSocketEvent — re-render on every tick
Bid
Ask
Last
Received
0
Renders
1
Symbol

Press Start to begin.

Batched
useSocketEventBatch — flushMs: 100
Bid
Ask
Last
Received
0
Renders
1
Last batch

Press Start to begin.

Per-event (the bad one)

PerEventQuote.tsx
function PerEventQuote({ manager }) {
  const [latest, setLatest] = useState<TServerMsg | null>(null);
  const [received, setReceived] = useState(0);

  useSocketEvent(manager, "tick", (msg) => {
    setLatest(msg);
    setReceived((n) => n + 1);
  });

  return <Quote bid={latest?.bid} ask={latest?.ask} last={latest?.last} />;
}

Batched (the right one)

BatchedQuote.tsx
function BatchedQuote({ manager }) {
  const [latest, setLatest] = useState<TServerMsg | null>(null);
  const [received, setReceived] = useState(0);

  useSocketEventBatch(
    manager,
    "tick",
    (msgs) => {
      if (msgs.length === 0) return;
      setReceived((n) => n + msgs.length);
      // Only the last value matters; intermediate ticks would just thrash the DOM
      setLatest(msgs[msgs.length - 1]);
    },
    { flushMs: 100 },
  );

  return <Quote bid={latest?.bid} ask={latest?.ask} last={latest?.last} />;
}

When you don't want batching

Batching is wrong when each event matters individually. Order fills, trade confirmations, position updates — anything you would log or alert on per event — must use useSocketEvent. Use batching only for "snapshot of the latest value" data.