Waiting for queries to become stale before they are fetched again doesn't always work, especially when you know for a fact that a query's data is out of date because of something the user has done. For that purpose, the QueryClient
has an invalidateQueries
method that lets you intelligently mark queries as stale and potentially refetch them too!
1// Invalidate every query in the cache
2queryClient.invalidateQueries()
3// Invalidate every query with a key that starts with `todos`
4queryClient.invalidateQueries({ queryKey: ['todos'] })
Note: Where other libraries that use normalized caches would attempt to update local queries with the new data either imperatively or via schema inference, TanStack Query gives you the tools to avoid the manual labor that comes with maintaining normalized caches and instead prescribes targeted invalidation, background-refetching and ultimately atomic updates.
When a query is invalidated with invalidateQueries
, two things happen:
- It is marked as stale. This stale state overrides any
staleTime
configurations being used inuseQuery
or related hooks - If the query is currently being rendered via
useQuery
or related hooks, it will also be refetched in the background
Query Matching with invalidateQueries
#
When using APIs like invalidateQueries
and removeQueries
(and others that support partial query matching), you can match multiple queries by their prefix, or get really specific and match an exact query. For information on the types of filters you can use, please see Query Filters.
In this example, we can use the todos
prefix to invalidate any queries that start with todos
in their query key:
1import { useQuery, useQueryClient } from '@tanstack/react-query'
2
3// Get QueryClient from the context
4const queryClient = useQueryClient()
5
6queryClient.invalidateQueries({ queryKey: ['todos'] })
7
8// Both queries below will be invalidated
9const todoListQuery = useQuery({
10 queryKey: ['todos'],
11 queryFn: fetchTodoList,
12})
13const todoListQuery = useQuery({
14 queryKey: ['todos', { page: 1 }],
15 queryFn: fetchTodoList,
16})
You can even invalidate queries with specific variables by passing a more specific query key to the invalidateQueries
method:
1queryClient.invalidateQueries({
2 queryKey: ['todos', { type: 'done' }],
3})
4
5// The query below will be invalidated
6const todoListQuery = useQuery({
7 queryKey: ['todos', { type: 'done' }],
8 queryFn: fetchTodoList,
9})
10
11// However, the following query below will NOT be invalidated
12const todoListQuery = useQuery({
13 queryKey: ['todos'],
14 queryFn: fetchTodoList,
15})
The invalidateQueries
API is very flexible, so even if you want to only invalidate todos
queries that don't have any more variables or subkeys, you can pass an exact: true
option to the invalidateQueries
method:
1queryClient.invalidateQueries({
2 queryKey: ['todos'],
3 exact: true,
4})
5
6// The query below will be invalidated
7const todoListQuery = useQuery({
8 queryKey: ['todos'],
9 queryFn: fetchTodoList,
10})
11
12// However, the following query below will NOT be invalidated
13const todoListQuery = useQuery({
14 queryKey: ['todos', { type: 'done' }],
15 queryFn: fetchTodoList,
16})
If you find yourself wanting even more granularity, you can pass a predicate function to the invalidateQueries
method. This function will receive each Query
instance from the query cache and allow you to return true
or false
for whether you want to invalidate that query:
1queryClient.invalidateQueries({
2 predicate: (query) =>
3 query.queryKey[0] === 'todos' && query.queryKey[1]?.version >= 10,
4})
5
6// The query below will be invalidated
7const todoListQuery = useQuery({
8 queryKey: ['todos', { version: 20 }],
9 queryFn: fetchTodoList,
10})
11
12// The query below will be invalidated
13const todoListQuery = useQuery({
14 queryKey: ['todos', { version: 10 }],
15 queryFn: fetchTodoList,
16})
17
18// However, the following query below will NOT be invalidated
19const todoListQuery = useQuery({
20 queryKey: ['todos', { version: 5 }],
21 queryFn: fetchTodoList,
22})