demo D92
Undo and redo in React
A history stack over edits, fills and pastes
grid.history
This grid keeps a history stack across edits, fills and pastes, so a user can step back out of a mistake and forward again. It gives direct manipulation a safety net, so people edit confidently without reloading data to recover.
This is the React version. The grid mounts through the createLatticeGrid adapter, which takes its configuration as ordinary props and hands back the live grid through a ref. The grid below is the same one every other tab runs.
The configuration
<link rel="stylesheet" href="https://cdn.latticegrid.dev/lattice-grid@1.107.0/lattice-grid.min.css">
<div id="grid"></div>
<script type="module">
import React from 'https://esm.sh/react@18';
import { createRoot } from 'https://esm.sh/react-dom@18/client';
import { createGrid } from 'https://cdn.latticegrid.dev/lattice-grid@1.107.0/lattice-grid.esm.min.js';
import createLatticeGrid from 'https://cdn.latticegrid.dev/lattice-grid@1.107.0/modules/react.esm.min.js';
const LatticeGrid = createLatticeGrid({ React, createGrid });
// One history stack over every kind of write: a typed cell, a fill, a paste
// and a range clear each land as one entry, so undo reverses the gesture
// rather than the cells it happened to touch. undoDepth sets how far back it
// reaches. The undo and redo buttons on the rail, and ctrl-z and ctrl-shift-z,
// both walk the same stack.
const columns = [
{ field: 'id', title: 'Order', layout: { width: 120, pin: 'start' } },
{ field: 'reference', title: 'Reference', layout: { width: 130 } },
{ field: 'team', title: 'Team', filter: { type: 'set' }, edit: true },
// A real dropdown: the stage is picked from a fixed list rather than typed,
// so the value is always one the grid knows about.
{ field: 'stage', title: 'Stage', filter: { type: 'set' },
lookup: { options: [
{ id: 'planned', label: 'Planned' },
{ id: 'approved', label: 'Approved' },
{ id: 'in-flight', label: 'In-flight' },
{ id: 'blocked', label: 'Blocked' },
{ id: 'done', label: 'Done' },
] },
edit: { enabled: true, editor: 'select', props: { allowClear: true } } },
{ field: 'due', title: 'Due', type: 'date', layout: { width: 140 }, edit: true },
{ field: 'circuits', title: 'Circuits', type: 'number', layout: { width: 110 }, edit: true },
{ field: 'impact', title: 'Impact', type: 'number', layout: { width: 100 }, edit: true },
{ field: 'effort', title: 'Effort', type: 'number', layout: { width: 100 }, edit: true },
{ field: 'budget', title: 'Budget', type: 'number', layout: { width: 140 },
format: { style: 'currency', currency: 'GBP' }, total: 'sum', edit: true },
{ field: 'notes', title: 'Notes', layout: { flex: 1, min: 220 }, edit: { enabled: true, editor: 'textarea' } },
];
const rows = [/* work orders */];
function App() {
return (
<LatticeGrid
rowKey="id"
selection={{ mode: 'multiple', ranges: true }}
edit={{ enabled: true, undoDepth: 100 }}
toolPanel={{ side: 'left', panels: ['columns'], actions: ['undo', 'redo', 'restore'], exportName: 'work-orders' }}
columns={columns}
rows={rows}
style={{ height: '540px' }}
/>
);
}
createRoot(document.getElementById('grid')).render(<App />);
</script>
Reversing edits with a grid-level history stack
Undo and redo give a user a way back out of a mistaken edit, fill, or paste without reloading data or hand-correcting cells. A developer reaches for this wherever the grid accepts direct manipulation rather than a form: inline editing, drag-fill, range paste, or a bulk edit. Lattice Grid tracks these mutations through grid.history, which records each committed change as a single entry regardless of how many cells it touched, so a paste covering four hundred cells undoes in one step rather than four hundred. The stack groups by transaction rather than by keystroke, which matters for a JavaScript data grid used for data entry: a user editing one cell across several keypresses gets one undo step for the commit, not one per character. Entries store the affected range and prior values rather than a full grid snapshot, so undoing a change on a hundred-thousand-row dataset costs the same as undoing one on a hundred-row dataset, and the operation replays through the same validation and change-detection path as a live edit, so a reverted cell re-triggers any dependent formula recalculation or row-level validation exactly as the original edit did. Keyboard handling follows platform convention, with Ctrl+Z and Ctrl+Shift+Z (or Cmd on macOS) wired to the stack by default and available as direct calls for a custom toolbar.
How do I add undo and redo to a JavaScript data grid?
Use grid.history, which maintains a stack of committed edits, fills, and pastes and exposes undo() and redo() alongside the default Ctrl+Z / Ctrl+Shift+Z bindings. Each stack entry represents one transaction, so a multi-cell paste or bulk edit reverses in a single step rather than one call per affected cell.