This documentation provides an overview of how to use the TanStack React Query library to fetch, cache, and manage server-side state in your React applications.
Table of Contents
- Installation
- Setting up React Query Provider
- Fetching Data Using
useQuery
- Handling Loading and Error States
- Using Query Keys
- Mutations with
useMutation
- Query Invalidation
- Polling and Refetching
- Optimistic Updates
- Further Resources
1. Installation
To start using React Query, install the package and its dependencies:
npm install @tanstack/react-query
If you are using TypeScript, you may also want to install the types:
npm install @types/react
2. Setting up React Query Provider
Wrap your application with the QueryClientProvider
to enable React Query throughout the app. You will need to create a QueryClient
instance.
// index.js or App.js
import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourComponent />
</QueryClientProvider>
);
}
export default App;
3. Fetching Data Using useQuery
The useQuery
hook is used to fetch and cache data from an API. It requires a query key (unique identifier) and a function to fetch the data.
import { useQuery } from '@tanstack/react-query';
const fetchPosts = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
if (!response.ok) throw new Error('Network response was not ok');
return response.json();
};
export function Posts() {
const { data, isLoading, error } = useQuery({ queryKey: ['posts'], queryFn: fetchPosts });
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
4. Handling Loading and Error States
React Query provides out-of-the-box support for handling loading and error states through the isLoading
and error
variables.
const { data, isLoading, error } = useQuery({ queryKey: ['posts'], queryFn: fetchPosts });
if (isLoading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error.message}</div>;
}
5. Using Query Keys
A query key is a unique identifier for the query. It helps React Query manage and cache the data efficiently. You can use strings or arrays for query keys.
useQuery( { queryKey: ['posts', { userId: 1 }], queryFn: fetchPostsByUser });
In the above example, the query key is ['posts', { userId: 1 }]
, making it unique for different users.
6. Mutations with useMutation
The useMutation
hook is used for operations that modify data, such as creating, updating, or deleting resources.
import { useMutation, useQueryClient } from '@tanstack/react-query';
const createPost = async (newPost) => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
body: JSON.stringify(newPost),
headers: { 'Content-Type': 'application/json' },
});
return response.json();
};
export function CreatePost() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: createPost,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['posts']});
},
});
const handleSubmit = () => {
mutation.mutate({ title: 'New Post', body: 'Post body' });
};
return <button onClick={handleSubmit}>Create Post</button>;
}
7. Query Invalidation
You can invalidate queries to refresh the data after mutations. This ensures that the cache stays up-to-date.
queryClient.invalidateQueries({ queryKey: ['posts']});
8. Polling and Refetching
React Query allows for automatic refetching at regular intervals to keep data fresh.
const { data, isLoading } = useQuery({
queryKey: ['posts'],
queryFn: fetchPosts
refetchInterval: 5000, // Refetch every 5 seconds
});
You can also manually refetch the data using the refetch
function provided by useQuery
.
const { data, refetch } = useQuery({ queryKey: ['posts'], queryFn: fetchPosts });
<button onClick={() => refetch()}>Refetch Posts</button>;
9. Optimistic Updates
Optimistic updates allow the UI to reflect the changes immediately, even before the mutation succeeds, improving user experience.
const mutation = useMutation(updatePost, {
onMutate: async (updatedPost) => {
await queryClient.cancelQueries(['posts']);
const previousPosts = queryClient.getQueryData(['posts']);
queryClient.setQueryData(['posts'], (old) =>
old.map((post) =>
post.id === updatedPost.id ? { ...post, ...updatedPost } : post
)
);
return { previousPosts };
},
onError: (err, updatedPost, context) => {
queryClient.setQueryData(['posts'], context.previousPosts);
},
onSettled: () => {
queryClient.invalidateQueries(['posts']);
},
});
10. Further Resources
- Official TanStack React Query Documentation
- React Query GitHub Repository
- React Query Devtools (For debugging React Query state)
This documentation should give you a solid starting point for working with React Query in your React applications. Let me know if you need further customization or examples!