Now that we have a basic understanding of useQuery hook, let's understand various ways through which we can configure our queries using the useQuery
hook.
useQuery(’key’, function, { options go here }):
Lets dive in
cacheTime
Let's try re-fetching the query results without useQuery hook first i.e. with a simple useEffect hook.
You’ll notice, that every time we do a re-fetch, the loading state is encountered each time. This shows the results are not being cached and as a result, a hard reload happens for that particular query.
Now let us try this re-fetch with useQuery hook. You'll notice below that the loading state is not encountered every time a re-fetch occurs. This indicates that query responses are being cached by React-Query by default.
You can also view the cached content maintained by navigating to Query.cache.queries[0].state.data
in the Query explorer section of devtools.
cacheTime
takes in number of milliseconds a query responses must be saved in the cache after which, the cache will be cleared and the query will be re-fetched automatically in the background.
By default, cachTime is set to 5 minutes.
How cool is that! All the cache management is done by React-Query itself.
You might wonder how will we come to know if the background re-fetch is being done because the isLoading
flag is not getting changed. For this specific reason React Query provides another flag isFetching
which indicates the background fetching status of the query.
Also for re-fetching, a refetch
function is returned that performs automatic re-fetch itself.
Below is the updated version of our code:
import React from "react";
import { useQuery } from "@tanstack/react-query";
async function fetchCompanies() {
return await fetch(`http://localhost:4000/companies`).then((response) =>
response.json()
);
}
const Companies = () => {
const {
isLoading,
data: companies,
isError,
error,
isFetching,
refetch,
} = useQuery(["companies"], fetchCompanies, {
cacheTime: 5000, // cache will be cleared after 5 seconds
});
if (isLoading) {
return <h1>loading</h1>;
}
if (isError) {
return <h1>{error.message}</h1>;
}
return (
<div>
{companies.map((company) => {
return <h1 key={company.id}>{company.name}</h1>;
})}
<button onClick={refetch}>refetch</button>
</div>
);
};
Note that, isFetching
indicates the background re-fetch status while isLoading
indicates the normal ones.
staleTime
staleTime
helps reduce the network requests to the server.
We specify staleTime in milliseconds after which the response from the query should be considered stale i.e. not fresh anymore.
By default staleTime is set to 0 milliseconds.
useQuery(["companies"], fetchCompanies, {
staleTime: 10000,
});
For instance, consider the above call, the staleTime is 10 seconds, and the response will be marked fresh
for the first 10 seconds, after which it will become stale
.
So any changes in the list of companies for the first 10 seconds will not be reflected and no matter how many times we hit /companies
endpoint no new network request will be generated, but once data becomes stale after 10 seconds updated list of companies will be fetched again in the background.
refetchOnMount
This option as the name suggests specifies whether to trigger re-fetch automatically when every time the component mounts. It can have 3 values:
-
true
: re-fetches on mount of the component only if the data is stale -
false
: does not re-fetches on mount even if data is stale -
always
: re-fetches every time on mount of the component irrespective of whether data stale or not.
The default value is set to true
and in most cases, it is best suited as well.
refetchOnWindowFocus
As the name suggests, a re-fetch occurs on focus of the window. This is similar to refetchonMount
and the values are same as well.
useQuery(["companies"], fetchCompanies, {
refetchOnMount: true,
refetchOnWindow: true,
});
refetchInterval
Sometimes we want to fetch the results from a query in regular intervals i.e. polling, we can do that by specifying the interval in milliseconds to this attribute.
Also by default, polling or automatic re-fetching is paused when the window loses focus, to prevent that from happening we can provide another config option called refetchIntervalInBackground
and set it to true
.
useQuery(["companies"\, fetchCompanies, {
refetchInterval: 5000,
refetchIntervalInBackground: true,
});
Here query will be fetched every 5 seconds irrespective of whether window is focused or not.
Can it get more easier than this!!? :)
enabled
You might have noticed as soon our component mounts the query is fetched and we get the results, but what if we want to trigger the fetch only if we click a button. enabled
prop will let us do that. By default it it set to true.
const Companies = () => {
const {
isLoading,
data: companies,
isError,
error,
isFetching,
refetch,
} = useQuery(["companies"], fetchCompanies, {
enabled: false
});
if (isLoading) {
return <h1>loading</h1>;
}
if (isError) {
return <h1>{error.message}</h1>;
}
return (
<div>
{companies?.map((company) => {
return <h1 key={company.id}>{company.name}</h1>;
})}
<button onClick={refetch}>fetch</button>
</div>
);
};
We use refetch
function, pass it as an onClick handler to the button, and set enabled to false
.
You can notice from devtools that initially when the component renders, the query key is marked as disabled
hence no network request is triggered.
but when we click on the fetch button, the network request is fired and data is fetched.
success
and error
callback
After fetching the response from the query you might want to perform some side effects based on whether the query was successful or not. For this react query provides success and error callbacks which will run automatically after the query fetch is complete and thus, we can perform any sideEffects.
const onSuccess = (data) => {
console.log(
"performing the side-effect after successful fetch with response ==> ",
data
);
};
const onError = (error) => {
console.log("encounter an error during fetching ==> ", error);
};
const { isLoading, data: companies, isError } = useQuery(
["companies"],
fetchCompanies, {
onSuccess,
onError,
});
It is evident from above that, data
and error
objects are implicitly injected in onSuccess
and onError
callbacks respectively.
select
In select
we define a function that is used to transform the data to our desirable format. For instance when we fetch the companies array, the response object looks like
{
"id": 3,
"name": "Apple",
"founder": "Steve Jobs"
}
Now suppose we want to print the founders instead of company names, we can transform the array of company objects to array of founders using select function
const { isLoading, data, isError} = useQuery(["companies"], fetchCompanies, {
select: (data) => {
const founders = data.map((company) => company.founder);
return founders;
},
});
Now the data returned from the useQuery
hook will have only array of founders. We can perform manipulation like filtering, reducing an array etc as per our requirements.
Thank you for reading!
In the next section, we'll learn about some approaches for querying the data, which are pretty common in web development and we'll see how easy React Query makes it for us to handle such situations.
Feel free to reach out to me! 😊
💻 Github ✨ Twitter 💌 Email 💡Linkedin
Till then happy coding!
Top comments (0)