Sync Grid with React State
ReoGrid Web is an imperative canvas component — it doesn’t re-render on every prop change like a virtual DOM list. The right pattern for integrating with React is a one-way subscription: the grid owns its data, React subscribes to changes.
Full example
import { useEffect, useRef, useState } from 'react';
import { Reogrid, type ReogridInstance } from '@reogrid/lite/react';
type Row = { product: string; price: number; qty: number };
const initialData: Row[] = [
{ product: 'Widget', price: 9.99, qty: 40 },
{ product: 'Gadget', price: 24.50, qty: 12 },
{ product: 'Gizmo', price: 39.00, qty: 7 },
];
export default function App() {
const gridRef = useRef<ReogridInstance>(null);
const [rows, setRows] = useState<Row[]>(initialData);
function onReady({ worksheet }: ReogridInstance) {
// 1. Initial load
['Product', 'Price', 'Qty'].forEach((h, c) =>
worksheet.cell(0, c).setValue(h).setStyle({ bold: true }),
);
initialData.forEach((row, i) => {
worksheet.cell(i + 1, 0).setValue(row.product);
worksheet.cell(i + 1, 1).setValue(String(row.price));
worksheet.cell(i + 1, 2).setValue(String(row.qty));
});
// 2. Subscribe to changes
worksheet.on('cellValueChange', ({ row, column, newValue }) => {
if (row === 0) return; // header row
const idx = row - 1;
setRows((prev) => {
const next = [...prev];
if (!next[idx]) return prev;
const updated = { ...next[idx] };
if (column === 0) updated.product = newValue;
if (column === 1) updated.price = Number(newValue) || 0;
if (column === 2) updated.qty = Number(newValue) || 0;
next[idx] = updated;
return next;
});
});
}
const total = rows.reduce((sum, r) => sum + r.price * r.qty, 0);
return (
<div>
<div style={{ padding: 12, fontSize: 18 }}>
Total: <strong>${total.toFixed(2)}</strong>
</div>
<Reogrid ref={gridRef} onReady={onReady} style={{ height: 400 }} />
</div>
);
}
The total in the header re-renders every time a cell is edited.
Pushing external updates back into the grid
When data changes outside the grid (from a WebSocket, a parent component, etc.) and you need to reflect it:
useEffect(() => {
const ws = gridRef.current?.worksheet;
if (!ws) return;
externalData.forEach((row, i) => {
ws.cell(i + 1, 0).setValue(row.product);
ws.cell(i + 1, 1).setValue(String(row.price));
ws.cell(i + 1, 2).setValue(String(row.qty));
});
}, [externalData]);
Do not drive this from React state that’s already synced from the grid — you’ll create an infinite loop. Only push back from sources external to the grid.
Common pitfalls
- Do not call grid methods during render.
gridRef.currentis null until afteruseEffectruns. Do setup insideonReadyor an effect. - Do not use
useImperativeHandle. The React wrapper assigns the ref insideuseEffect, which runs afteruseImperativeHandle’s factory. Read the ref directly. - Do not make the grid a controlled component. Treating each cell as a React-owned value defeats the canvas renderer’s performance. The grid is the source of truth for cell data; React state holds derived/summary values.