patterntypescriptreactModerate
TanStack Query — cache invalidation and refetching after mutations
Viewed 0 times
useMutationinvalidateQueriessetQueryDatacache invalidationonSuccessisPendingrefetch after mutation
Problem
After a create/update/delete mutation, the cache still holds the old data. Without explicit invalidation, the UI shows stale data until the cache expires.
Solution
Use useMutation with onSuccess to invalidate related queries:
import { useMutation, useQueryClient } from '@tanstack/react-query';
async function createPost(body: { title: string; content: string }) {
const res = await fetch('/api/posts', {
method: 'POST',
body: JSON.stringify(body),
headers: { 'Content-Type': 'application/json' },
});
if (!res.ok) throw new Error('Failed to create post');
return res.json();
}
function NewPostForm() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: createPost,
onSuccess: (newPost) => {
// Invalidate the posts list — triggers a background refetch
queryClient.invalidateQueries({ queryKey: ['posts'] });
// OR: update the cache directly (avoids a round-trip)
queryClient.setQueryData<Post[]>(['posts'], (old = []) => [...old, newPost]);
},
onError: (error) => {
console.error('Mutation failed:', error);
},
});
return (
<button
onClick={() => mutation.mutate({ title: 'Hello', content: 'World' })}
disabled={mutation.isPending}
>
{mutation.isPending ? 'Saving...' : 'Create Post'}
</button>
);
}
import { useMutation, useQueryClient } from '@tanstack/react-query';
async function createPost(body: { title: string; content: string }) {
const res = await fetch('/api/posts', {
method: 'POST',
body: JSON.stringify(body),
headers: { 'Content-Type': 'application/json' },
});
if (!res.ok) throw new Error('Failed to create post');
return res.json();
}
function NewPostForm() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: createPost,
onSuccess: (newPost) => {
// Invalidate the posts list — triggers a background refetch
queryClient.invalidateQueries({ queryKey: ['posts'] });
// OR: update the cache directly (avoids a round-trip)
queryClient.setQueryData<Post[]>(['posts'], (old = []) => [...old, newPost]);
},
onError: (error) => {
console.error('Mutation failed:', error);
},
});
return (
<button
onClick={() => mutation.mutate({ title: 'Hello', content: 'World' })}
disabled={mutation.isPending}
>
{mutation.isPending ? 'Saving...' : 'Create Post'}
</button>
);
}
Why
invalidateQueries marks matching cached queries as stale and refetches them if they have active subscribers. setQueryData is a faster alternative that updates the cache synchronously, but requires the returned data to exactly match the query shape.
Gotchas
- invalidateQueries uses prefix matching — invalidating ['posts'] also invalidates ['posts', 123]
- setQueryData does not trigger a network request — the cache is updated optimistically; combine with invalidation if you need server confirmation
- onSuccess receives the data returned by mutationFn — ensure the API returns the created/updated resource
- mutation.isPending replaced mutation.isLoading in TanStack Query v5
Revisions (0)
No revisions yet.