BACK
Use casesUndo/redo — a history of form changes
Use cases · 14

Undo/redo — a history of form changes

Pattern

An editor, a builder, a complex form. The user should be able to undo a change (Ctrl+Z) and redo it (Ctrl+Y). The model: three lists — past, present, future.

The problem it solves

If you keep the history in arrays and mutate them from button handlers, sooner or later past and future fall out of sync. Especially after a new edit on top of an old undo: the classic bug is forgetting to clear future, so redo applies a "ghost" from a deleted branch of history.

Operators and why they matter

  • Subject — receives commands: edit, undo, redo, snapshot.
  • scan — this is our reducer. It holds {past, present, future} and applies commands as a pure function.
  • map — extracts present for the template.
  • withLatestFrom — needed for the snapshot: "grab the current present at the moment of the save command".

Gotchas

  • After a new edit you must clear future. Otherwise redo will restore state from a deleted branch of history.
  • The reducer must be pure (no in-place array mutations). Otherwise OnPush change detection misses the update.
  • A BehaviorSubject with manual next works too, but a scan-reducer is cleaner to test with marble tests.

What you get

The history becomes a predictable state machine without imperative flags. The undo/redo logic is described by one pure function.

script.ts // TypeScript
CONSOLE · Console output
Hit Run to see the result...