Managing Dependent Queries in TanStack Query

In applications where one query depends on the result of another, managing dependent queries is essential. TanStack Query provides tools to handle such scenarios with ease, ensuring efficient data fetching and proper synchronization.


This tutorial will guide you through:

  • What dependent queries are.
  • Using the enabled option to control query execution.
  • Examples for practical implementation.


Step 1: What Are Dependent Queries?

Dependent queries occur when:

One query relies on the output of another query.

A query should only execute when specific conditions are met (e.g., data from another query).

Example Scenarios:

Fetching tasks for a specific project after retrieving the project list.

Loading a user's profile details after selecting a user.

Step 2: Setting Up TanStack Query

Install TanStack Query

npm install @tanstack/react-query

npm install @tanstack/react-query-devtools

Setup QueryClient

Wrap your application with the QueryClientProvider.


App.js

import React from 'react';

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

import { ProjectTasks } from './ProjectTasks';


const queryClient = new QueryClient();


function App() {

  return (

    <QueryClientProvider client={queryClient}>

      <h1>Dependent Queries Example</h1>

      <ProjectTasks />

      <ReactQueryDevtools initialIsOpen={false} />

    </QueryClientProvider>

  );

}


export default App;


Step 3: Using the enabled Option

The enabled option controls whether a query should run. It can be dynamically set based on the availability of required data.

Example 1: Fetching Tasks for a Selected Project

Fetch the list of projects.

Fetch tasks for the selected project only after a project is chosen.

ProjectTasks.js

import React, { useState } from 'react';

import { useQuery } from '@tanstack/react-query';


const fetchProjects = async () => {

  const response = await fetch('https://api.example.com/projects');

  if (!response.ok) throw new Error('Failed to fetch projects');

  return response.json();

};


const fetchTasks = async (projectId) => {

  const response = await fetch(`https://api.example.com/projects/${projectId}/tasks`);

  if (!response.ok) throw new Error('Failed to fetch tasks');

  return response.json();

};


export function ProjectTasks() {

  const [selectedProject, setSelectedProject] = useState(null);


  // Fetch project list

  const { data: projects, isLoading: projectsLoading } = useQuery(['projects'], fetchProjects);


  // Fetch tasks for the selected project

  const { data: tasks, isLoading: tasksLoading } = useQuery(

    ['tasks', selectedProject],

    () => fetchTasks(selectedProject),

    {

      enabled: !!selectedProject, // Only run query if a project is selected

    }

  );


  if (projectsLoading) return <p>Loading projects...</p>;


  return (

    <div>

      <h2>Select a Project</h2>

      <ul>

        {projects.map((project) => (

          <li key={project.id}>

            <button onClick={() => setSelectedProject(project.id)}>

              {project.name}

            </button>

          </li>

        ))}

      </ul>


      {selectedProject && tasksLoading && <p>Loading tasks...</p>}

      {tasks && (

        <ul>

          {tasks.map((task) => (

            <li key={task.id}>{task.title}</li>

          ))}

        </ul>

      )}

    </div>

  );

}


Explanation:

The enabled option ensures the fetchTasks query only runs when selectedProject is not null.

Tasks are fetched dynamically based on the selected project.

Example 2: Fetching User Details After Selecting a User

Fetch a list of users.

Fetch detailed user information after selecting a user.

UserDetails.js

import React, { useState } from 'react';

import { useQuery } from '@tanstack/react-query';


const fetchUsers = async () => {

  const response = await fetch('https://jsonplaceholder.typicode.com/users');

  if (!response.ok) throw new Error('Failed to fetch users');

  return response.json();

};


const fetchUserDetails = async (userId) => {

  const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);

  if (!response.ok) throw new Error('Failed to fetch user details');

  return response.json();

};


export function UserDetails() {

  const [selectedUser, setSelectedUser] = useState(null);


  // Fetch user list

  const { data: users, isLoading: usersLoading } = useQuery(['users'], fetchUsers);


  // Fetch user details for the selected user

  const { data: userDetails, isLoading: detailsLoading } = useQuery(

    ['userDetails', selectedUser],

    () => fetchUserDetails(selectedUser),

    {

      enabled: !!selectedUser, // Only fetch details if a user is selected

    }

  );


  if (usersLoading) return <p>Loading users...</p>;


  return (

    <div>

      <h2>Select a User</h2>

      <ul>

        {users.map((user) => (

          <li key={user.id}>

            <button onClick={() => setSelectedUser(user.id)}>{user.name}</button>

          </li>

        ))}

      </ul>


      {selectedUser && detailsLoading && <p>Loading user details...</p>}

      {userDetails && (

        <div>

          <h3>{userDetails.name}</h3>

          <p>Email: {userDetails.email}</p>

          <p>Phone: {userDetails.phone}</p>

        </div>

      )}

    </div>

  );

}


Step 4: Combining Dependent Queries

You can chain multiple dependent queries by dynamically enabling them based on their dependencies.


Example:

  • Fetch projects.
  • Fetch tasks for the selected project.
  • Fetch detailed task information for a selected task.
  • Best Practices for Dependent Queries

Use Descriptive Query Keys: Clearly define query keys to avoid conflicts (e.g., ['tasks', projectId]).

Leverage the enabled Option: Ensure queries run only when necessary to avoid unnecessary API calls.

Provide Default States: Use conditional rendering to handle cases where dependent data is not yet available.

Error Handling: Handle errors gracefully at each query level using isError and error.


In this tutorial, you learned how to:

  • Use the enabled option to manage dependent queries.
  • Chain queries to fetch data in a logical sequence.
  • Handle dynamic data requirements efficiently.

TanStack Query simplifies dependent query management, ensuring your application fetches data only when required. Hope this is helpful, and I apologize if there are any inaccuracies in the information provided.

Comments

Popular posts from this blog

Integrating PHP with Message Queues RabbitMQ Kafka

FastAPI and UVLoop: The Perfect Pair for Asynchronous API Development

Konfigurasi dan Instalasi PostgreSQL Secara Lengkap di Windows Linux dan MacOS