BlogReactJS

Force refetch with React Query

Written by Codemzy on December 4th, 2023

You can invalidate an active query to get it to force refetch with React Query. Here's how you can force refetch after a mutation, or on a button click, to check for updated data.

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>
 );
};