Skip to content

Streaming Data

The StreamingAdapter enables live data updates to the engine without full re-initialization. Push new rows, update existing ones, or delete rows — changes are batched within a configurable throttle window before writing to CellStore and triggering a single render.

pushRows/updateRow/deleteRow → pending queue → [throttle window] → batch apply → CellStore → render

Multiple calls within the throttle window (default: 100ms) are coalesced into a single batch write, minimizing render cycles.

import { WitEngine, StreamingAdapter } from '@witqq/spreadsheet';
const adapter = new StreamingAdapter(engine, {
columnKeys: ['id', 'name', 'price', 'quantity'],
throttleMs: 100, // default: 100ms
});
interface StreamingAdapterOptions {
columnKeys: string[];
throttleMs?: number;
}
OptionTypeDefaultDescription
columnKeysstring[]requiredColumn keys in order, matching engine column indices
throttleMsnumber100Throttle window in ms for batching updates

Append rows to the end of the grid:

adapter.pushRows([
{ id: 101, name: 'New Item', price: 29.99, quantity: 5 },
{ id: 102, name: 'Another Item', price: 14.50, quantity: 12 },
]);

Rows are queued and applied after the throttle window. The engine’s row count increases automatically.

Update an existing row by logical index. Partial updates supported — only specified keys are modified:

adapter.updateRow(5, { price: 39.99, quantity: 0 });

The logical index is translated to a physical row via DataView, so updates work correctly even when sort/filter is active.

Delete a row by logical index:

adapter.deleteRow(10);

Deletion shifts all subsequent rows up in CellStore and updates RowStore height overrides. The engine row count decreases automatically.

Immediately apply all pending updates, bypassing the throttle timer:

adapter.flush();

Flush pending updates and prevent further scheduling:

adapter.dispose();
console.log(adapter.disposed); // true

After applying a batch, the adapter emits a cellChange event with source: 'streaming-adapter' and sentinel values (row: -1, col: -1). Use this to detect batch completions:

engine.getEventBus().on('cellChange', (event) => {
if (event.source === 'streaming-adapter') {
console.log('Streaming batch applied');
}
});

updateRow and deleteRow use DataView.getPhysicalRow() internally, so they work correctly when sort or filter is active. The logical index you pass corresponds to the visible (sorted/filtered) order.

After a streaming batch, sort/filter state is preserved — the engine re-renders with updated data in the current sort/filter order. To re-sort after updates, trigger sort manually via SortEngine.

const ws = new WebSocket('wss://data-feed.example.com');
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
switch (msg.type) {
case 'insert':
adapter.pushRows(msg.rows);
break;
case 'update':
adapter.updateRow(msg.rowIndex, msg.data);
break;
case 'delete':
adapter.deleteRow(msg.rowIndex);
break;
}
};
// Clean up on disconnect
ws.onclose = () => adapter.dispose();
  • Event SystemcellChange events from streaming updates
  • DataView — logical↔physical row mapping used by streaming
  • Change Tracking — cell status lifecycle for server sync