I’ve written before about cacheTime
and staleTime
in React Query, and how to customise them to your data storage needs - but what if things change?
Here’s a quick summary of how refetching works in React Query. Firstly, even if you leave the default staleTime
as 0 (so the data is immediately stale) it won’t refetch unless the query becomes inactive. And that makes total sense, otherwise, you have React Query constantly refetching the same data over and over again, eating up bandwidth and wasting resources.
So instead it fetches the data for a query, you can display that data, and then if you go away from the data, or to another tab, when you come back it will refetch. Nice!
But what if you want to force a refetch while the query is active?
How to force refetch
import { useQueryClient } from '@tanstack/react-query';
// get the query client
const queryClient = useQueryClient();
// invalidate and force refetch a query
queryClient.invalidateQueries({
queryKey: ['your-key'],
refetchType: 'all'; // refetch both active and inactive queries
});
In the above code, we use queryClient.invalidateQueries
to invalidate the queryKey
, and set refetchType
to 'all'
so that the query will refetch whatever status it is (active or inactive).
The invalidateQueries
method doesn’t just mark your query as stale, for a refetch later. It tells React Query that this query is now invalid, and needs a refetch.
If you only want to force refetch on the active query, you can set refetchType
to 'active'
or just remove the refetchType
option since this is the default behaviour anyway.
The
invalidateQueries
method can be used to invalidate and refetch single or multiple queries in the cache based on their query keys or any other functionally accessible property/state of the query. By default, all matching queries are immediately marked as invalid and active queries are refetched in the background.— React Query -
queryClient.invalidateQueries
If you want to force a refetch while the query is being used, you can trigger that with an action (much like the default refetchOnMount
, refetchOnReconnect
, and refetchOnWindowFocus
actions available on useQuery
). So let’s look at a couple of examples, forcing a refetch after a mutation, and forcing a refetch when a button is clicked.
If you want to force refetch at regular intervals, you can use refetchInterval
or refetchIntervalInBackground
options on useQuery
. This blog post will look at forcing a refetch with a button click or following a mutation instead.
Force refetch examples
Let’s imagine you’re building a todo app (because if you’re a coder, at some point you are gonna build a todo app!). Let’s say you fetch your to-do list, and since they don’t change that often, you set staleTime
to 10 minutes. And that works fine, except if the user adds a todo.
If the user adds a to-do, you want to force a refetch of that to-do list.
Sure, you could add it client side, but where does it belong in the list? What order is the list in? Is the new to-do for today, tomorrow, or some other day? All of that logic and organisation is on the server, and instead of re-coding it on the client, you can just refetch the todos!
And, you know, some of your users might use this to-do app on a couple of devices. And so you want to add a button to refresh the list. Because, if they add a todo, you want to give them a way to force a refresh on the other device too.
Force refetch on useMutation
When something happens like adding a todo, where you will useMutation
to send the data to the server, you can use the onSuccess
option to force a refetch for the query.
import { useMutation, useQueryClient } from '@tanstack/react-query';
// api call
function addTodo(todo) {
return axiosInstance.post('/api/todos', todo);
};
// add todo mutation hook
function useAddTodo() {
// get the query client
const queryClient = useQueryClient();
// create the mutation
return useMutation((todo) =>
addTodo(todo), {
onSuccess: (data) => {
// invalidate to force refetch
queryClient.invalidateQueries({ queryKey: ['todos'] })
},
}
);
};
The code above will force a refetch on the active query. If you have multiple pages of data in React Query, you can choose to force refetch all the data (including inactive queries that match the query key) with the option refetchType: 'all'
as discussed earlier or leave this as default to refetch the other pages on the query when (and if) they become active again.
Did you know you can invalidate multiple queries by their prefix? Get more info on specifying which queries to invalidate in ReactQuery with queryClient.invalidateQueries().
Force refetch on button click
To force a refetch in React Query on a button click, you can use the same code as above. But instead of invalidating on a successful mutation, you could do it in an onClick
handler.
import { useQueryClient } from '@tanstack/react-query';
function App() {
// get the query client
const queryClient = useQueryClient();
// force refetch
function handleClick() {
// invalidate to force refetch
queryClient.invalidateQueries({ queryKey: ['todos'] });
}
return (
<button onClick={handleClick}>Refetch</button>
);
};