Documentation: Using TanStack React Query in React

October 21, 2024 (2mo ago)

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

  1. Installation
  2. Setting up React Query Provider
  3. Fetching Data Using useQuery
  4. Handling Loading and Error States
  5. Using Query Keys
  6. Mutations with useMutation
  7. Query Invalidation
  8. Polling and Refetching
  9. Optimistic Updates
  10. 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


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!