No history yet

Introduction to React Query

What Is React Query?

Think of your app's server state like a library. Fetching data with useEffect is like constantly checking out the same book, reading it, and returning it, even if you just read it a minute ago. It's inefficient and repetitive.

React Query, now part of the TanStack Query library, acts as a smart librarian for your data. It fetches data for you, but it also caches it, keeps it fresh in the background, and updates it when needed. You just ask for the data, and React Query handles the complex logistics of making sure you have the most up-to-date version without unnecessary requests.

React Query is a tool that simplifies data fetching, caching, and synchronization in your React apps.

It gives you a simple set of hooks that manage the complexities of server state, letting you focus on building your user interface.

Why Bother With React Query?

Fetching data in React often involves a combination of useState and useEffect. This approach works, but it quickly becomes cumbersome. You have to manually manage loading states, error states, and the data itself. What happens when the user navigates away and then comes back? You fetch the same data all over again.

Here's a typical data-fetching pattern using only React hooks:

import { useState, useEffect } from 'react';

function MyComponent() {
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);
  const [data, setData] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch('https://api.example.com/data');
        const json = await response.json();
        setData(json);
      } catch (error) {
        setError(error);
      } finally {
        setIsLoading(false);
      }
    };

    fetchData();
  }, []);

  if (isLoading) return 'Loading...';
  if (error) return 'An error has occurred: ' + error.message;

  return <div>{/* Render your data here */}</div>;
}

That's a lot of boilerplate code for one simple request. You have to repeat this logic for every piece of server data you need.

React Query handles all of this for you. It simplifies data fetching, caching, and state management, making your code cleaner and more efficient. It automatically handles refetching data when it becomes stale, retrying failed requests, and even optimizing performance with features like pagination and infinite scrolling.

React Query streamlines server state management by abstracting away the repetitive tasks of managing loading, error, and success states.

Core Concepts

To get started with React Query, you need to understand a few key concepts.

Query

noun

A query is a declarative dependency on an asynchronous source of data. In simpler terms, it's a request to fetch data from your server. Queries are tied to a unique key.

You use the useQuery hook to fetch data. It returns an object containing the status of the query (isLoading, isError, isSuccess) and the data itself. The first argument is a unique key for the query, and the second is the function that fetches the data.

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

function Todos() {
  const { isLoading, error, data } = useQuery({ 
    queryKey: ['todos'], 
    queryFn: fetchTodoList 
  });

  if (isLoading) return 'Loading...';

  if (error) return 'An error has occurred: ' + error.message;

  return (
    <ul>
      {data.map(todo => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
}

Mutation

noun

A mutation is used for creating, updating, or deleting data on the server. While queries are for reading data, mutations are for writing or changing it.

The useMutation hook gives you a mutate function to call when you're ready to make your change. It also provides status indicators like isLoading and isError.

A key feature here is query invalidation. After a mutation succeeds (like adding a new item to a list), you can tell React Query that certain queries are now stale. This automatically triggers a refetch for those queries, ensuring your UI always shows the latest data.

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

function AddTodo() {
  const queryClient = useQueryClient();

  const mutation = useMutation({ 
    mutationFn: postTodo, // function to post new todo
    onSuccess: () => {
      // Invalidate and refetch the 'todos' query
      queryClient.invalidateQueries({ queryKey: ['todos'] });
    },
  });

  return (
    <button 
      onClick={() => {
        mutation.mutate({ id: new Date(), title: 'Do Laundry' });
      }}
    >
      Add Todo
    </button>
  );
}

Setting Up a React Project

Getting started is straightforward. First, create a new React application using Create React App or Vite:

# Using npm with Create React App
npx create-react-app my-react-query-app

# Or using Vite (recommended)
npm create vite@latest my-react-query-app -- --template react

Next, navigate into your new project directory and install React Query:

cd my-react-query-app
npm install @tanstack/react-query

To make React Query available throughout your app, you need to wrap your application in a QueryClientProvider. You'll typically do this in your main.jsx or index.js file.

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import {
  QueryClient,
  QueryClientProvider,
} from '@tanstack/react-query';

// Create a client
const queryClient = new QueryClient();

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </React.StrictMode>
);

With this setup, you can now use the useQuery and useMutation hooks in any component within your app. You've offloaded the tedious parts of data fetching and can now build features faster and with more confidence.

Quiz Questions 1/6

The provided text compares React Query to a "smart librarian". What does this analogy primarily highlight about React Query's function?

Quiz Questions 2/6

Which hook is used to modify server data (e.g., creating, updating, or deleting items)?