Undo/redo requires tracking state changes.
Command pattern:
interface Command {
execute(): void;
undo(): void;
}
class SetCellCommand {
constructor(cellId, oldValue, newValue) {...}
execute() { setCellValue(cellId, newValue); }
undo() { setCellValue(cellId, oldValue); }
}
History stack:
{ past: [cmd1, cmd2], future: [] }
Batch operations: Group related changes (paste multiple cells) into single undo step.