State Management with Zustand and React QueryLesson 6.4
React Query mutations: creating, updating, and deleting data
useMutation hook, mutate function, onSuccess callback, onError callback, optimistic updates, invalidateQueries, mutation state, mutateAsync
Mutations with useMutation
useMutation handles data writes: POST, PUT, DELETE requests. After a successful mutation, invalidate related queries to refetch fresh data automatically.
Basic Mutation
import { useMutation, useQueryClient } from '@tanstack/react-query';
function AddTodo() {
const queryClient = useQueryClient();
const { mutate, isPending, isError } = useMutation({
mutationFn: (newTodo) =>
fetch('/api/todos', {
method: 'POST',
body: JSON.stringify(newTodo),
headers: { 'Content-Type': 'application/json' },
}).then(r => r.json()),
onSuccess: () => {
// Invalidate the todos query so the list refetches
queryClient.invalidateQueries({ queryKey: ['todos'] });
},
onError: (error) => {
console.error('Failed to add todo:', error);
},
});
function handleSubmit(e) {
e.preventDefault();
mutate({ text: 'New todo', completed: false });
}
return (
<form onSubmit={handleSubmit}>
<button disabled={isPending}>
{isPending ? 'Adding...' : 'Add Todo'}
</button>
{isError && <p>Failed to add.</p>}
</form>
);
}Use mutateAsync when you need to await the mutation result in a try/catch. Use mutate when you handle callbacks via onSuccess/onError. Always invalidate related query keys after successful writes so the UI reflects the latest server state.
