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
Post a Comment