Mastering Query Keys in TanStack Query for Optimal Caching
Query keys are fundamental to how TanStack Query caches, organizes, and identifies data. They enable efficient caching, updating, and invalidation of queries, making them an essential tool for managing server state effectively.
In this tutorial, you will learn:
- What query keys are and how they work.
- How to structure query keys for optimal caching.
- Practical examples of query key usage.
Step 1: What Are Query Keys?
A query key is a unique identifier for a query. It ensures that data fetched from a server is stored and retrieved correctly from the cache. Query keys can be simple strings or complex arrays.
Examples:
Simple key: ['users']
Key with parameters: ['user', userId]
Query keys are essential because:
They identify unique data sets in the cache.
They enable efficient cache invalidation and refetching.
Step 2: Setting Up TanStack Query
Before diving into query keys, ensure your project is set up with TanStack Query.
Install TanStack Query
npm install @tanstack/react-query
npm install @tanstack/react-query-devtools
Set Up the Query Client
App.js
import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<h1>Mastering Query Keys</h1>
<UsersList />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
export default App;
Step 3: Using Simple Query Keys
A simple query key is a single identifier for fetching data.
Example: Fetching All Users
UsersList.js
import React from 'react';
import { useQuery } from '@tanstack/react-query';
const fetchUsers = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
return response.json();
};
export function UsersList() {
const { data, isLoading } = useQuery(['users'], fetchUsers);
if (isLoading) return <p>Loading users...</p>;
return (
<ul>
{data.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
Explanation:
The query key ['users'] uniquely identifies this data set.
Data is fetched once and cached for reuse.
Step 4: Structuring Query Keys with Parameters
Use query keys with parameters to fetch and cache data for specific entities or subsets.
Example: Fetching a Single User
UserDetails.js
import React from 'react';
import { useQuery } from '@tanstack/react-query';
const fetchUserById = async ({ queryKey }) => {
const [, userId] = queryKey;
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
return response.json();
};
export function UserDetails({ userId }) {
const { data, isLoading } = useQuery(['user', userId], fetchUserById);
if (isLoading) return <p>Loading user details...</p>;
return (
<div>
<h2>{data.name}</h2>
<p>Email: {data.email}</p>
<p>Phone: {data.phone}</p>
</div>
);
}
Explanation:
The query key ['user', userId] ensures each user's data is stored separately.
TanStack Query can differentiate between ['user', 1] and ['user', 2].
Step 5: Query Key Best Practices
1. Use Descriptive Keys
Ensure keys are descriptive and meaningful.
['products', categoryId]
2. Maintain Consistent Structures
Keep query keys consistent throughout your application.
3. Leverage Nesting for Complex Data
For hierarchical or related data:
['projects', projectId, 'tasks']
Step 6: Invalidating Query Keys
Invalidate specific queries to refetch fresh data.
Example: Invalidating a Query
AddUserForm.js
import React, { useState } from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
const addUser = async (newUser) => {
const response = await fetch('https://jsonplaceholder.typicode.com/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newUser),
});
return response.json();
};
export function AddUserForm() {
const [name, setName] = useState('');
const queryClient = useQueryClient();
const mutation = useMutation(addUser, {
onSuccess: () => {
queryClient.invalidateQueries(['users']); // Invalidate the 'users' query
},
});
const handleSubmit = (e) => {
e.preventDefault();
mutation.mutate({ name });
setName('');
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter name"
/>
<button type="submit">Add User</button>
</form>
);
}
Explanation:
invalidateQueries(['users']) marks the ['users'] query as stale, triggering a refetch.
Step 7: Dependent Queries
Use query keys to handle dependent queries where one query relies on another's result.
Example: Fetching Tasks for a Project
const fetchTasks = async ({ queryKey }) => {
const [, projectId] = queryKey;
const response = await fetch(`https://api.example.com/projects/${projectId}/tasks`);
return response.json();
};
export function ProjectTasks({ projectId }) {
const { data, isLoading } = useQuery(['project', projectId, 'tasks'], fetchTasks);
if (isLoading) return <p>Loading tasks...</p>;
return (
<ul>
{data.map((task) => (
<li key={task.id}>{task.title}</li>
))}
</ul>
);
}
Explanation:
Query key ['project', projectId, 'tasks'] ensures tasks are cached per project.
By mastering query keys, you can:
- Organize cached data efficiently.
- Fetch data dynamically based on parameters.
- Use query keys for targeted cache invalidation.
With this understanding, you can harness TanStack Query's full potential for optimal caching and performance in your applications. Hope this is helpful, and I apologize if there are any inaccuracies in the information provided.
Comments
Post a Comment