Skip to main content

Roll back an optimistic React update when a save fails

2026-11-11 16:36:28.919129+00:00
Roll back an optimistic React update when a save fails

An optimistic toggle makes a task look complete before the server confirms it. If the save fails, the application needs enough information to restore a coherent view. A delayed error is especially confusing when the completed task has already disappeared from the current filter.

For a synthetic task list, record the previous cached value before applying the optimistic change. The basic sequence is: cancel conflicting refetches, capture a snapshot, update the cache, restore the snapshot on error, and reconcile with the server when the mutation settles.

TanStack Query's optimistic-update guide documents this cache approach and also describes a simpler option that displays pending mutation variables directly in the UI.

Optimistic state moving forward and rolling back on failure.

Follow one rejected toggle

Begin with task 17 marked incomplete. Configure a local save stub to reject after a short delay. Click its checkbox once. The list can immediately show the task as complete, but it should also make the pending state understandable.

When the rejection arrives, restore the previous task value and display an error near the action. Keep the task's identity stable so keyboard focus does not jump to another row. A global toast alone can be difficult to connect to the affected item when several tasks are visible.

For a disposable fixture, log the task ID and mutation ID at each stage. The useful observation is whether a specific rejected operation returns the UI to its expected state. A screenshot of the initial optimistic change proves little about recovery.

Handle another click while the first save waits

A whole-list snapshot becomes risky when multiple optimistic changes overlap. Suppose toggle A captures a snapshot, then toggle B changes another task. Restoring A's old list after A fails can erase B's successful update.

One small design is to disable further changes to the affected item until its save settles. Changes to other items still require rollback scoped to the failed operation. More involved interfaces can keep a queue of pending operations and derive the displayed state from confirmed data plus that queue.

A network timeout is also ambiguous: the server may have committed the change before the connection failed. Use an explicit desired value such as "completed: true" and an operation identifier when the API supports idempotent processing. Blindly sending "toggle again" can reverse a save that already happened.

After an error, a refetch helps establish the authoritative server state. It can fail too, so preserve a visible unresolved status until reconciliation succeeds. Optimistic feedback changes perceived responsiveness; it does not remove the need to explain pending work or verify persistence.