A beginner's guide to react query (Tanstack V5), part 2: The query client.

Emmanuel Nnajiofor
6 min readApr 15, 2024

--

Hi there, this post is part of a series on react-query. The aim is to help us understand ReactQuery in detail. Feeling lost? — you can start here.

Photo by Josh Redd on Unsplash

In this post, we would be taking a closer look at the QueyClient . The QueryClient is a central component in React Query that manages caching, fetching, and updating data. It acts as a global store for query results and provides various options that help us describe how we want our queries and mutations to behave.

In our last post, we looked at three of these options; retry , staleTime and refetchOnWindowFocus

const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 1,
staleTime: 30000,
refetchOnWindowFocus: false,
},
mutations: {
//...
}
},
});

Beyond these three, we would be discussing other options provided within the queries and mutations object.

const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 2,
staleTime: 5000, // 5 seconds
refetchOnMount: true,
refetchOnWindowFocus: true,
refetchOnReconnect: true,
refetchInterval: 5000, //5 seconds
refetchIntervalInBackground: false,
},
mutations: {
retry: 2,
},
},
});

Queries

Contains settings for how we want our queries to behave whenever we use useQuery in our application.

retry — This option specifies the number of attempts a query will automatically make in case of a prior failed attempt. In this example, queries will retry up to 2 times. This option can also take boolean values or a fucntion that returns a boolean. If the boolean is false the query would not make another attempt after failure. In the case where the value is true it would continue to make infinite retries until it gets a successful response.

With a function, we are able to access values like the failureCount and error. These gives use more flexibility, as we can decide what to do with those values in different situations

const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: (failureCount, error) => {
//...do something with passed argument
},
// ...
},
// ...
},
});

In this case, queries would be retried until the function return false.

staleTime — Sets the time in milliseconds after which the cached data is considered stale. Think of staleTime as setting an expiration date for our cached data. When data becomes stale, it means it might not be the most up-to-date version. This option tells React Query how long it can trust the data in its cache before considering it old and needing a refresh.

Say we set staleTime: 5000. This means our cached data is considered fresh for 5000 milliseconds (or 5 seconds). During this time, React Query will happily serve up this data to our app without bothering the server. But once those 5 seconds are up, it'll start thinking, "Hmm, maybe it's time to check if there's fresher data available." and it’ll make a query for new data.

refetchOnMount — This option is like a welcoming committee, ensuring our app greets its users with the freshest data as soon as they arrive. Controls whether a query should be automatically refetched when the component mounts.

Setting refetchOnMount: true means React Query will kick off a fresh data fetch every time a component is mounted. This ensures that whenever our users land on a page or view, they're met with the most up-to-date information available.

refetchOnWindowFocus — Specifies whether a query should be refetched when the window or tab gains focus. Setting it to true ensures that the query is refetched whenever the focus returns to the window or tab. Setting it as false, disables automatic refetching on window focus, meaning data won't be refreshed when users return to the app window.

refetchOnReconnect — Picture this scenario: a user’s internet connection drops, then reconnects. They expect the app to update with fresh data, right? That’s precisely what refetchOnReconnect handles. It triggers a data refresh whenever the app reconnects to the internet after a connection loss.Setting this option as true ensures that whenever our users regain connectivity, they’re presented with the most recent data available.

refetchInterval — We want our app to automatically fetch fresh data at regular intervals, ensuring users always have the latest information.

It can receive a number or boolean value. Setting a numeric value in milliseconds specifies the time interval between data refetches. For example, refetchInterval: 5000 would trigger a data refresh every 5 seconds. Setting it to true enables automatic data refetching at the default interval specified by React Query.

refetchIntervalInBackground — Keeps data fresh behind the scenes. This option acts as a silent caretaker, quietly ensuring our app’s data stays fresh even when it’s not in the spotlight. In scenarios where users may switch between apps or leave our app running in the background, refetchIntervalInBackground ensures that data remains fresh and relevant.

It can receive a number or boolean value. Setting a numeric value specifies the time interval between data refetches, even when the app is in the background. For instance, refetchIntervalInBackground: 60000 would trigger a data refresh every 60 seconds.

Setting true enables automatic data refetching at the default interval specified by React Query, even when the app is in the background.

Mutations

The mutations object offers a range of options to customize the behavior of mutation operations. By utilizing these options in the mutations object of our QueryClient setup, we can customize the behavior of mutation operations to suit our specific application requirements. Whether it’s handling errors gracefully, updating UI components, or performing cleanup tasks, React Query provides the flexibility we need to create robust and responsive applications. Let’s take a closer look at some of the options

const queryClient = new QueryClient({
defaultOptions: {
queries: {
//...
},
mutations: {
retry: 2,
retryDelay: (attemptIndex) => attemptIndex * 1000, // Increase delay with each retry
onMutate: async (variables) => {
// Perform tasks before mutation operation
},
onError: (error, variables, context) => {
// Handle errors gracefully
},
onSuccess: (data, variables, context) => {
// Update UI components after successful mutation
},
onSettled: (data, error, variables, context) => {
// Perform cleanup tasks after mutation operation
},
},
},
});

retry — Similar to its counterpart in queries, the retry option for mutations specifies the number of attempts a mutation will automatically make in case of a prior failed attempt. This ensures that our app can recover from transient failures, such as network errors.

retryDelay — The retryDelay option complements retry by specifying the delay (in milliseconds) between retry attempts. This can be particularly useful for mitigating issues such as rate limiting or server congestion.

onMutate — This option allows us to specify a function to run before the mutation operation begins. It’s useful for tasks like updating local state or performing optimistic updates.

onError — This option allows us to specify a function to run before the mutation operation begins. It’s useful for tasks like updating local state or performing optimistic updates.

onSuccess — onSuccess allows us to specify a function to run after the mutation operation succeeds. This can include updating UI components or triggering other actions.

onSettled — The onSettled option lets us define a function to run after the mutation operation completes, whether it succeeds or fails. It’s useful for performing cleanup tasks.

Putting it all together

const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 2,
staleTime: 5000, // 5 seconds
refetchOnMount: true,
refetchOnWindowFocus: true,
refetchOnReconnect: true,
refetchInterval: 5000, //5 seconds
refetchIntervalInBackground: false,
},
mutations: {
retry: 2,
retryDelay: (attemptIndex) => attemptIndex * 1000, // Increase delay with each retry
onMutate: async (variables) => {
// Perform tasks before mutation operation
},
onError: (error, variables, context) => {
// Handle errors gracefully
},
onSuccess: (data, variables, context) => {
// Update UI components after successful mutation
},
onSettled: (data, error, variables, context) => {
// Perform cleanup tasks after mutation operation
},
},
},
});

With this setup, our React Query-powered application is equipped to handle a variety of scenarios with finesse. From keeping data fresh and responsive to gracefully managing mutation operations, React Query’s QueryClient options offer the flexibility and control we need to deliver exceptional user experiences.

--

--