TanStack Query (Formerly React Query): The Basics

TanStack Query (formerly known as React Query) is a robust library for managing server state in React applications. It streamlines data fetching, caching, synchronization, and more. Having used this library in recent projects, I’m excited to share some of its core concepts. Below, I’ll walk you through the fundamentals of TanStack Query, including queries, mutations, and query invalidation, and provide practical examples to help you integrate these features into your code effectively.

What is a Query?

In TanStack Query, a query is used to fetch and cache data. It allows you to define what data to fetch and how to handle it within your application. Queries are ideal for operations that do not modify data but need to fetch or read it.

Example: Fetching User Information

Suppose you want to fetch user information from an API. You can use a query to accomplish this:


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

const fetchUser = async (userId: string) => {
  const { data } = await axios.get(`/api/users/${userId}`);
  return data;
};

const UserComponent: React.FC<{ userId: string }> = ({ userId }) => {
  const { data, isLoading, isError, error } = useQuery(['user'], () => fetchUser(userId));

  if (isLoading) {
    //Show loading indicator
  }

  if (isError {
    //Show the use an error modal
  }

return (

  {`Hello ${user.userName}`}

)

 

In the above example:

  • [‘user’] is the query key we created, used to uniquely identify the query and cache the data (more on this later!).
  • useQuery provides states such as isLoading, isError, data, and others via object destructuring. This is especially helpful to subscribe only to the fields you actually need.

What is a Mutation?

A mutation is used for creating, updating, or deleting data. Unlike queries, mutations are used to change data on the server.

Example: Create a New User

Suppose you need to create a new user for your application:


import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';

const createUser = async (user: { name: string }) => {
  const { data } = await axios.post('/api/users', user);
  return data;
};

const CreateUserComponent: React.FC = () => {
  const queryClient = useQueryClient();
  const mutation = useMutation(createUser, {
    onSuccess: () => {
      // Invalidate queries to refetch updated data
      queryClient.invalidateQueries(['users']);
    },
  });

  const handleSubmit = async () => {
    mutation.mutate({ name: 'New User' });
  };

return(
  //UI of your component
)

In this example:

  • createUser is the function used to create a new user.
  • queryClient is used to access the `QueryClient` instance. This instance is essential for managing query and mutation data. It controls the cache, fetching, and state of all queries and mutations.
  • onSuccess is used to define what should happen when the mutation is successful. Here we are invalidating the `fetchUser` query which will refetch the list of users. Note that there are several other options you can use to handle different states of a mutation which can be found here.

Query Invalidation

Query invalidation is the process of marking a query as stale, prompting it to refetch data. This part was a little nebulous for me at first, so to put it more simply: writing a mutation changes the data in your database. We need a way of knowing when the data has changed. And, once we know it has changed, we need to get the most up-to-date data so we can display that to our users. Once you’re comfortable with it, this is an incredibly useful feature you’ll never be able to live without.

There are a few different ways of writing the syntax for invalidating a query:


import { useQueryClient } from '@tanstack/react-query';
const queryClient = useQueryClient();

// Invalidate all queries
await queryClient.invalidateQueries();

// Invalidate queries that match a specific query key
await queryClient.invalidateQueries(['users']);

// Invalidate queries that exactly match a specific query key
await queryClient.invalidateQueries({
  queryKey: ['users', 1],
  exact: true,
});

Properties for Invalidating Queries:

  • queryKey: Specifies which queries to invalidate. This can be a partial key (e.g., [‘users’]) or an exact key (e.g., [‘users’, 1]). Note that the first argument to the query key array should be a unique string or identifier to ensure that queries are uniquely identifiable and managed correctly by the query client.
  • exact: If true, only queries with the exact queryKey will be invalidated. If false, any query that partially matches the queryKey will be invalidated.

TanStack Query simplifies data fetching and state management with its powerful querying and mutation capabilities. Understanding queries, mutations, and query invalidation helps ensure your application remains up-to-date and responsive. By handling multiple items in query keys and leveraging query invalidation, you can build efficient and effective data-driven applications.

Conversation
  • Herbert says:

    Great overview of TanStack Query! It’s fascinating how the library simplifies data fetching and state management for React applications.

  • URL says:

    Your writing is always so engaging and informative. Love it!

  • Join the conversation

    Your email address will not be published. Required fields are marked *