Capítulo 29 de 80
When a mutation's response already contains the updated object, write it directly into the cache with setQueryData instead of invalidating and refetching — it's the same data, so the extra network call is pure waste; updates must always be applied immutably.
onSuccess: (data) => queryClient.setQueryData(['todo', {id: 5}], data) updates the matching query's cache entry with the mutation's own response, skipping a redundant refetch for data you already have.setQueryData pairing in a custom hook (e.g. useMutateTodo) so every call site gets the cache-sync behavior for free; the onSuccess callback's second argument (variables) is what the mutate() call was invoked with, useful for building the target key dynamically (['todo', { id: variables.id }]).setQueryData's updater function must return a new object/array rather than mutating the oldData argument in place — in-place mutation can appear to work but causes subtle bugs (structural-sharing/reference-equality assumptions break silently).const useMutateTodo = () => {
const queryClient = useQueryClient()
return useMutation({
mutationFn: editTodo,
onSuccess: (data, variables) => {
queryClient.setQueryData(['todo', { id: variables.id }], data)
},
})
}
// Correct: immutable update
queryClient.setQueryData(['posts', { id }], (oldData) =>
oldData ? { ...oldData, title: 'my new post title' } : oldData
)
setQueryData's updater function must follow.setQueryData from a mutation's response over invalidateQueries whenever the response already is the fresh data — it's strictly cheaper.oldData argument inside a setQueryData updater — always return a new object/array, even when just changing one field.