docs/framework/solid/guides/optimistic-updates.md
const addTodoMutation = useMutation(() => ({
mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }),
// make sure to _return_ the Promise from the query invalidation
// so that the mutation stays in `pending` state until the refetch is finished
onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
}))
<ul>
<For each={todoQuery.data}>{(todo) => <li>{todo.text}</li>}</For>
<Show when={addTodoMutation.isPending}>
<li style={{ opacity: 0.5 }}>{addTodoMutation.variables}</li>
</Show>
</ul>
<Show when={addTodoMutation.isError}>
<li style={{ color: 'red' }}>
{addTodoMutation.variables}
<button onClick={() => addTodoMutation.mutate(addTodoMutation.variables)}>
Retry
</button>
</li>
</Show>
// somewhere in your app
const mutation = useMutation(() => ({
mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }),
onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
mutationKey: ['addTodo'],
}))
// access variables somewhere else
const variables = useMutationState(() => ({
filters: { mutationKey: ['addTodo'], status: 'pending' },
select: (mutation) => mutation.state.variables,
}))
const queryClient = useQueryClient()
useMutation(() => ({
mutationFn: updateTodo,
// When mutate is called:
onMutate: async (newTodo, context) => {
// Cancel any outgoing refetches
// (so they don't overwrite our optimistic update)
await context.client.cancelQueries({ queryKey: ['todos'] })
// Snapshot the previous value
const previousTodos = context.client.getQueryData(['todos'])
// Optimistically update to the new value
context.client.setQueryData(['todos'], (old) => [...old, newTodo])
// Return a result with the snapshotted value
return { previousTodos }
},
// If the mutation fails,
// use the result returned from onMutate to roll back
onError: (err, newTodo, onMutateResult, context) => {
context.client.setQueryData(['todos'], onMutateResult.previousTodos)
},
// Always refetch after error or success:
onSettled: (data, error, variables, onMutateResult, context) =>
context.client.invalidateQueries({ queryKey: ['todos'] }),
}))
useMutation(() => ({
mutationFn: updateTodo,
// When mutate is called:
onMutate: async (newTodo, context) => {
// Cancel any outgoing refetches
// (so they don't overwrite our optimistic update)
await context.client.cancelQueries({ queryKey: ['todos', newTodo.id] })
// Snapshot the previous value
const previousTodo = context.client.getQueryData(['todos', newTodo.id])
// Optimistically update to the new value
context.client.setQueryData(['todos', newTodo.id], newTodo)
// Return a result with the previous and new todo
return { previousTodo, newTodo }
},
// If the mutation fails, use the result we returned above
onError: (err, newTodo, onMutateResult, context) => {
context.client.setQueryData(
['todos', onMutateResult.newTodo.id],
onMutateResult.previousTodo,
)
},
// Always refetch after error or success:
onSettled: (newTodo, error, variables, onMutateResult, context) =>
context.client.invalidateQueries({ queryKey: ['todos', newTodo.id] }),
}))
useMutation(() => ({
mutationFn: updateTodo,
// ...
onSettled: async (newTodo, error, variables, onMutateResult, context) => {
if (error) {
// do something
}
},
}))