Client and server state Recommended
Undo optimistic updates that fail
When the screen changes before the server answers, keep a copy of the old data. If the request fails, put it back and say so in words. Refetch either way.
How to do it
Use optimistic updates only for small actions that nearly always succeed, such as a star, a rename or a checkbox. Never use them for payments, sends or deletes that others see at once. Cancel running fetches for the same key first, so they don't overwrite the change. React's useOptimistic does the same for one component.
Example
export function useRenameProject(accountId: string) {
return useMutation({
mutationFn: ({ id, name }: { id: string; name: string }) => api.renameProject(accountId, id, name),
onMutate: async ({ id, name }, context) => {
const key = ['accounts', accountId, 'projects', id];
await context.client.cancelQueries({ queryKey: key });
const previous = context.client.getQueryData<Project>(key);
context.client.setQueryData<Project>(key, old => old && { ...old, name });
return { key, previous };
},
onError: (_error, _input, saved, context) => {
if (saved) context.client.setQueryData(saved.key, saved.previous);
announce('The new name was not saved.');
},
onSettled: (_data, _error, _input, saved, context) => saved && context.client.invalidateQueries({ queryKey: saved.key }),
});
}
@app.patch("/api/projects/<project_id>")
def rename_project(project_id):
project = projects.rename(current_user().account_id, project_id, request.json["name"])
return project.to_json(), 200, {"ETag": f'"{project.version}"'}
How to check
A test makes the server answer 500 to a rename. The old name comes back within one second, a message says the change was not saved, and the input keeps focus.
For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers app interface playbook: https://agentwranglers.se/playbook/app-ui/client-and-server-state/optimistic-with-rollback.