Undo after a restart
sync.undo() reverts the user's last change, and the history it works from is saved on the device.
Close the app, open it tomorrow, and the user can still undo yesterday's last steps, whether or not
they have synced. This page is what is saved, where, and when it is dropped.
What a step is
One undoable write (a create, an update, a delete or a whole transaction) is one step. A step is not a snapshot of the data. It keeps the operations of that write, each with what it replaced:
| The write was… | The step remembers… |
|---|---|
| An update | The previous values of the fields it changed |
| A create | That the row was created |
| A delete | The whole row, so undo can bring it back |
When the user presses undo, Ferry builds the reverse from the rows as they are now and sends it as a new write. That is why a step never goes stale: if another device changed a different field in the meantime, undo leaves that change alone.
Try it. Edit, undo and redo, then restart the app with persist on and off:
Task title: Book flights
Undo stack
empty
Redo stack
empty
Where it lives
The history lives in the client's storage, next to the rows and the outbox,
under keys that start with sync.undo. It is written together with the change it belongs to, so a
crash cannot leave a write without its undo step.
Which storage you pick decides what survives:
| Storage | After a restart |
|---|---|
| IndexedDB, SQLite, MMKV, localStorage | The history is still there |
Memory (the default of createSync) | Nothing: the data is gone as well |
The options
import { createSync, indexedDbStorage } from 'ferrysync';
import { schema } from './schema';
export const sync = createSync({
schema,
storage: indexedDbStorage('tasks'),
undo: { limit: 50, persist: true },
});| Option | Default | What it does |
|---|---|---|
limit | 100 | The most steps kept, undo and redo together. The oldest go first |
persist | true | Saves the history in storage. false keeps it in memory only |
What leaves the history
- The oldest step, when the limit is reached.
- A write the server refuses: there is nothing left to undo.
- The steps of a space the user loses access to, since undoing them would write somewhere the user can no longer see.
- Everything, on
connectandreset, and after an update that changes the schema version.
Things to know
- Deleted rows stay on the device. A delete step keeps the whole row so it can come back. If
rows are sensitive, set
persist: false: it also clears whatever an earlier run stored. - The history belongs to the device. Another device never sees it; the undo itself is an ordinary write that syncs like any other.
- Writes that are not the user's (a migration, a settings sync) should pass
{ undoable: false }so undo does not offer them.
Next
- Transactions and undo: the calls,
useUndoandundoable. - Storages: choosing where the data lives.