Skip to content

Change Tracking

ChangeTracker monitors cell modifications and maintains a status lifecycle for each changed cell. This enables visual indicators (colored cell borders/backgrounds) that show users which cells have been modified, are being saved, or encountered errors.

undefined → 'changed' → 'saving' → 'saved'
↘ 'error'
  • undefined — Cell has not been modified since baseline
  • changed — Cell value differs from baseline
  • saving — Save operation in progress
  • saved — Successfully persisted (auto-clears after short delay)
  • error — Save operation failed
Live Demo
Edit cells to see 'changed' status (blue border). Click Save to see saving → saved lifecycle.
Edit cells, then click "Simulate Save" to see the status lifecycle.
View source code
ChangeTrackingDemo.tsx
import { useRef, useEffect, useState } from 'react';
import { WitTable } from '@witqq/spreadsheet-react';
import type { WitTableRef } from '@witqq/spreadsheet-react';
import { DemoWrapper } from './DemoWrapper';
import { generateEmployees, employeeColumns } from './generate-data';
import { useSiteTheme } from './useSiteTheme';
const data = generateEmployees(30);
export function ChangeTrackingDemo() {
const { witTheme } = useSiteTheme();
const tableRef = useRef<WitTableRef>(null);
const [status, setStatus] = useState('Edit cells, then click "Simulate Save" to see the status lifecycle.');
useEffect(() => {
const engine = tableRef.current?.getInstance();
if (!engine) return;
engine.getChangeTracker().captureBaseline();
}, []);
const handleSimulateSave = async () => {
const engine = tableRef.current?.getInstance();
if (!engine) return;
const tracker = engine.getChangeTracker();
const changed = tracker.getChangedCells();
if (changed.length === 0) {
setStatus('No changes to save. Edit some cells first.');
return;
}
setStatus(`Saving ${changed.length} cell(s)...`);
for (const cell of changed) {
tracker.setCellStatus(cell.row, cell.col, 'saving');
}
engine.requestRender();
await new Promise(r => setTimeout(r, 1000));
for (const cell of changed) {
tracker.setCellStatus(cell.row, cell.col, 'saved');
}
engine.requestRender();
setStatus(`Saved ${changed.length} cell(s). Status will clear shortly.`);
};
return (
<DemoWrapper title="Live Demo" description="Edit cells to see 'changed' status (blue border). Click Save to see saving → saved lifecycle." height={440}>
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<div style={{ padding: '0.5rem 0.75rem', borderBottom: '1px solid #e2e8f0', flexShrink: 0, display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
<button onClick={handleSimulateSave} style={{ padding: '4px 12px', cursor: 'pointer' }}>💾 Simulate Save</button>
<span style={{ fontSize: '0.8rem', color: '#64748b' }}>{status}</span>
</div>
<div style={{ flex: 1 }}>
<WitTable
theme={witTheme}
ref={tableRef}
columns={employeeColumns}
data={data}
showRowNumbers
editable
style={{ width: '100%', height: '100%' }}
/>
</div>
</div>
</DemoWrapper>
);
}
import { useRef } from 'react';
import { WitTable, WitTableRef } from '@witqq/spreadsheet-react';
function App() {
const ref = useRef<WitTableRef>(null);
const handleSave = async () => {
const tracker = ref.current?.getInstance().getChangeTracker();
const changed = tracker?.getChangedCells() ?? [];
for (const cell of changed) {
tracker?.setCellStatus(cell.row, cell.col, 'saving');
}
try {
await saveToBackend(changed);
for (const cell of changed) {
tracker?.setCellStatus(cell.row, cell.col, 'saved');
}
} catch (err) {
for (const cell of changed) {
tracker?.setCellStatus(cell.row, cell.col, 'error');
}
}
};
return (
<>
<button onClick={() => ref.current?.getInstance().getChangeTracker().captureBaseline()}>
Capture Baseline
</button>
<button onClick={handleSave}>Save Changes</button>
<WitTable ref={ref} columns={columns} data={data} editable={true} />
</>
);
}

Use setCellStatus to integrate with your backend save mechanism:

// Mark cells as saving
tracker.setCellStatus(row, col, 'saving');
// On success
tracker.setCellStatus(row, col, 'saved');
// On failure
tracker.setCellStatus(row, col, 'error');
MethodSignatureDescription
captureBaseline() => voidSnapshot current state as baseline
setCellStatus(row: number, col: number, status: CellStatus) => voidSet cell status
getCellStatus(row: number, col: number) => CellStatus | undefinedGet cell status
getChangedCells() => Array<{ row: number; col: number }>List all modified cells
clearChanges() => voidClear all tracking data