Querying By Id
Now that we know about working with useQuery
hook and its configuration options to fetch the data, we can move forward to what is also pretty common operation i.e. to fetch any particular item details from a list of items based on its id
.
Consider another endpoint /companies/:id
that returns clicked company details.
<h1 key={company.id}>
<Link to={`/companies/${company.id}`}>{company.name}</Link>
</h1>
import React from "react";
import { useParams } from "react-router-dom";
import { useQuery } from "@tanstack/react-query";
const fetchCompany = (id) => {
return fetch(`http://localhost:4000/companies/${id}`).then((response) =>
response.json()
);
};
const Company = () => {
const { id } = useParams();
const {
isLoading,
data: companyData,
isError,
error,
} = useQuery(["company", id], () => fetchCompany(id));
if (isLoading) {
return <h1>loading</h1>;
}
if (isError) {
return <h1>{error.message}</h1>;
}
return (
<div>
<h2>{companyData.name}</h2>
<h2>{companyData.ceo}</h2>
</div>
);
};
export default Company;
Let us understand what happened above,
First we define the fetchCompany
function that takes in the id
of the company, fetches the company details, and returns it.
Second, To get the clicked company’s id we can use useParams
hook provided by react-router-dom
that returns an object which contains all URL parameters, we just destructed id param.
Now lets look at the useQuery
hook,
const {
isLoading,
data: companyData
isError
} = useQuery(["company", id], () => fetchCompany(id));
The hook has minor changes, the query key now constitutes of a string key and id param, this helps to uniquely identify the query. So the key takeaway from this is that the query key here is ["company", id]
.
Now in the second argument, we provide the callback function with the company id.
Result
It works!
Notice how we’re passing the id
to the fetchCompnay
function in the callback. Their’s an aleternative way to achieve that without passing the id
in the callback in the useQuery
hook.
React-Query will pass some meta information and query key as arguments to the fetch function implicitly, so in the fetch function we can de-structure the appropriate arguments to get the id of the company.
Since the query key is an array which gets passed in the queryKey
props, from there we can get the id
of the company.
const fetchCompany = ({ queryKey }) => {
const id = queryKey[1];
return fetch(`http://localhost:4000/companies/${id}`).then((response) =>
response.json()
);
};
const {
isLoading,
data: companyData
isError
} = useQuery(["company", id], fetchCompany);
Dynamic Parallel Queries
Now that we know how to fetch an item based on its id
, consider a scenario where the requirement is to fetch multiple items based on their respective ids parallelly. Most developers (including me) might think of putting the useQuery hook in a loop and calling it multiple times but that is not something that is allowed as it violates the rule of hooks i.e. they cannot be called inside any conditional statements or for loops.
Hence to tackle this specific scenario react query provides a hook called useQueries
, lets see how it works
import { useQueries } from "@tanstack/react-query";
const componyIds = [1, 2, 3];
const response = useQueries({
queries: componyIds.map((id) => {
return {
queryKey: ["company", id],
queryFn: fetchCompany,
staleTime: Infinity
};
})
});
The useQueries
hook accepts an options
object with a queries key whose value is an array with query option objects identical to the useQuery
hook
All queries run in parallel and a response is returned only after all companies are fetched.
Dependant Queries
Now that we know how to handle multiple queries parallely, we are also going to come across scenarios where we have to fetch the data sequentially i.e. when one query is dependent on another query before it can fetch the data.
Dependent (or serial) queries depend on previous ones to finish before they can execute. To achieve this, it's as easy as using the enabled
option to tell a query when it is ready to run.
For this case let's say we have /githubUsers
endpoint which returns a list of Github users, where each user being:
{
"id": "jack@gmail.com",
"githubId": "gf6ds5g4d5sf1g65dsf"
},
and we have /githubRepositories
endpoint which returns the user data like:
{
"id": "gf6ds5g4d5sf1g65dsf",
"repositories": [
"jack/react",
"jack/next",
"jack/angular",
"jack/vue"
]
}
This is a simple scenario where dependent queries come into the picture. We cannot fetch the user repositories until we have the id of the user.
Using enabled
config we choose to pause the execution of a query until the desired data is available.
const fetchGithubUser = ({ queryKey }) => {
const userEmail = queryKey[1];
return fetch(`http://localhost:4000/githubUsers/${userEmail}`).then(
(response) => response.json()
);
};
const fetchRepositories = ({ queryKey }) => {
const userId = queryKey[1];
return fetch(`http://localhost:4000/githubRepositories/${userId}`).then(
(response) => response.json()
);
};
const GihubUserRepos = () => {
const userEmail = "jack@gmail.com";
const { data: user } = useQuery(["github-user", userEmail], fetchGithubUser);
const githubUserId = user?.githubId;
const { data: userData } = useQuery(
["github-user-data", githubUserId], fetchRepositories, {
enabled: !!githubUserId,
}
);
const userRepos = userData?.repositories;
console.log({ userRepos });
return (
<div>
// map through the list of userRepos
</div>
);
Notice how we first fetch the Github user based on an email which in turn will not be immediately available so we use optional chaining to get the user id.
While fetching the repositories we provide the enabled
config with value !!githubUserId
, we use double negation to convert the id
to a boolean value.
Initially, the user id is not available when the component mounts hence userId
is kept as null
, and react query tracks the query as disabled
. Then once the githubId
is available, that is used to fetch the user repositories.
That’s how dependant queries are managed by React Query.
Thank you for reading!
In this lesson, we went through a couple of ways using which we can tackle different data fetching problems that developers face while building applications.
In the next lesson, we will go through some advanced concepts which can enhance and improve the overall data fetching/viewing experience for users.
Feel free to reach out to me! 😊
💻 Github ✨ Twitter 💌 Email 💡Linkedin
Till then happy coding!
Top comments (0)