Another way we can improve the data fetching/viewing experience of the user is by providing Initial Query Data in config options to the useQuery
hook. Let us first consider why we need it in the first place.
We know when we hit /companies
endpoint we get a list of companies where each company looks like:
{
"id": 1,
"name": "Microsoft",
"ceo": "Satya Nadela"
}
Now consider another /companyDetails
endpoint that fetches the details of the company with companies looking as:
{
"id": 1,
"name": "Microsoft",
"ceo": "Satya Nadela",
"founder": "Bill Gates",
"headQuarters": "Redmond, Washington, United States"
}
Note that some data like name
, id
, and ceo
is common between the above 2 company objects.
Now let us use useQuery
hook to fetch the company details using /companyDetails
endpoint
const fetchCompanyDetails = ({ queryKey }) => {
const id = queryKey[1];
return fetch(`http://localhost:4000/companyDetails/${id}`).then((response) =>
response.json()
);
};
const {
isLoading,
data: companyDetails
} = useQuery(["company-details", id], fetchCompanyDetails);
Note: For testing purposes, I have set the network throttling to Slow 3G in the network tab of Chrome Devtools.
From the above, we can note that each time the user clicks on the company name the loading state is shown and then complete company details are then displayed to the user.
This is where intialData
can help. It prevents the loading state to be shown to the user by showing the user, already cached information thereby thus improving the overall data fetching/viewing experience.
Now that we know why we need initial query data let us see how to use it.
import React from "react";
import { useParams } from "react-router-dom";
import { useQuery, useQueryClient } from "@tanstack/react-query";
const fetchCompanyDetails = ({ queryKey }) => {
const id = queryKey[1];
return fetch(`http://localhost:4000/companyDetails/${id}`).then((response) =>
response.json()
);
};
const Company = () => {
const { id } = useParams();
const queryClient = useQueryClient();
const {
isLoading,
data: companyData,
} = useQuery(["company-details", id], fetchCompanyDetails, {
initialData: () => {
const cachedCompanyData = queryClient
.getQueryData("companies")
.find((cachedCompany) => cachedCompany.id === parseInt(id));
console.log({ cachedCompanyData });
return cachedCompanyData ? cachedCompanyData : undefined;
},
});
if (isLoading) {
return <h1>loading</h1>;
}
return (
<div>
<h2>name: {companyData.name}</h2>
<h2>ceo: {companyData.ceo}</h2>
<h2>founder: {companyData.founder}</h2>
<h2>headquarters: {companyData.headQuarters}</h2>
</div>
);
};
export default Company;
We can provide initialData
config to the useQuery hook which is a function, whatever we return from this function will serve as the initial data for the page but if we return undefined
then react query sets our app in a hard loading state until the complete data is fetched thus preventing from errors.
In our case since we already have some data to be shown to the user, we get that data from the cache using queryClient.getQueryData("companies")
this returns a list of companies in which we perform a search operation to find the clicked company using the id
which is accessible using useParams
hook if in case the cache is empty then we return undefined
else we return the cached company data. Hereβs what the result looks like
Notice how the loading state is not shown this time and the user is shown the cached information while the data is being fetched in the background and shown once done as if it is being fetched on the go.
Thank you for reading!
In the next lesson, we will tackle one of the most common UI pattern problem that every Frontend Developer has faced which is performing paginated and infinite queries.
We'll see how easy React Query makes it for us to perform such operations.
Feel free to reach out to me! π
π» Github β¨ Twitter π Email π‘Linkedin
Till then happy coding!
Top comments (0)