DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 966,155 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for Querying data using React Query
Nischal Dutt
Nischal Dutt

Posted on

Querying data using React Query

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>
Enter fullscreen mode Exit fullscreen mode
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;
Enter fullscreen mode Exit fullscreen mode

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));
Enter fullscreen mode Exit fullscreen mode

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

querying by id using useQuery

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);
Enter fullscreen mode Exit fullscreen mode

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
    };
  })
});
Enter fullscreen mode Exit fullscreen mode

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

dynamic parallel queries

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"
},
Enter fullscreen mode Exit fullscreen mode

and we have /githubRepositories endpoint which returns the user data like:

{
  "id": "gf6ds5g4d5sf1g65dsf",
  "repositories": [
    "jack/react",
    "jack/next",
    "jack/angular",
    "jack/vue"
  ]
}
Enter fullscreen mode Exit fullscreen mode

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>
    );
Enter fullscreen mode Exit fullscreen mode

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.

dependant queries using enabled

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)

Need a better mental model for async/await?

Check out this classic DEV post on the subject.

β­οΈπŸŽ€ JavaScript Visualized: Promises & Async/Await

async await