Systems guy dabbling in web here. Optimistic UI kept biting me: the happy path looked instant, but a failed delete left a ghost row, or two rapid edits raced and the cache ended up in a state that matched neither the server nor the user's intent.
I wrote a prompt that makes the model treat the cache mutation, the rollback snapshot, and the reconciliation on settle as one unit, and forbids it from just refetching everything to paper over the race. The generated hook is small and finally survives me spamming the button.
Is there a cleaner pattern than snapshotting the whole list for rollback?
Implement create/update/delete for a `Task` resource in React using optimistic updates. Assume TanStack Query. Correctness over cleverness.
FOR EACH mutation, follow this exact lifecycle:
1. onMutate: cancel outgoing refetches for the affected query key, snapshot the previous cache value, apply the optimistic change, and return the snapshot as context.
2. onError: roll back to the snapshot from context. Never leave a partial state.
3. onSettled: invalidate the query key so the server becomes the source of truth once the dust settles.
HARD RULES:
- No full-list refetch inside onMutate to 'fix' state; the optimistic update must be a precise local edit.
- Handle concurrent mutations: if two are in flight, rollback of one must not clobber the other's optimistic change. Snapshot per-mutation, not globally.
- The delete case must not resurrect the row on a slow network if a later refetch is stale.
- Strict TypeScript, explicit types for the cache shape, no `any`.
Deliver a `useTasks` query hook and `useCreateTask`, `useUpdateTask`, `useDeleteTask`. Add a short note on how the concurrent-mutation case is handled.