Optimistic updates
Show a change instantly, roll back on failure, reconcile with the server on success — then optionally invalidate to confirm against server truth (the TanStack pattern).
Try it live
A todo list over a simulated API (~600 ms delay). Adds show up instantly as a dimmed optimistic item, then solidify when the server commits. Flip the toggle to make the server reject writes and watch the rollback; fire several adds quickly — TAKE_EVERY keeps a separate optimistic layer per mutation.
The pattern
import { createQuery, createMutation, optimisticUpdate, invalidate } from 'effector-refetch';
const todosQuery = createQuery({ effect: fetchTodosFx });
const addTodo = createMutation({ effect: addTodoFx });
optimisticUpdate({
query: todosQuery,
on: addTodo,
// applied immediately on addTodo.mutate(...)
update: ({ data, params }) => [{ id: -1, text: params.text, pending: true }, ...(data ?? [])],
// reconcile the temp item with the server result on success
commit: ({ data, result }) => (data ?? []).map((t) => (t.id === -1 ? result : t)),
// rollbackOnFailure defaults to true
});
// reconcile against server truth as well
invalidate({ on: addTodo, refetch: todosQuery });
addTodo.mutate({ text: 'Buy milk' });How it works
- On
addTodostart, the pre-mutation$datais snapshotted as the base (once per burst of in-flight mutations) and this mutation's optimistic layer is stacked on top. - On failure — or an aborted run (
enabledgate skip,TAKE_LATESTsupersede) — only that mutation's layer is removed; the remaining in-flight layers are re-applied over the base. - On success, the layer is materialized into the base (
commitreconciles it with the server result; withoutcommitthe optimistic value is kept). cancel/resetroll back all in-flight layers at once.
Parallel (TAKE_EVERY) mutations are safe: each keeps its own layer, so one failure can't wipe another's optimistic value or the original data. Runnable demo: examples/optimistic-parallel.ts.
WARNING
With out-of-order settles the layers are re-applied in start order — if your update functions don't commute, the composition may differ from the server's; reconcile via commit or pair with invalidate when exact ordering matters. In-flight layers are matched to settles by their params (stable JSON), so identical params pair FIFO.