React Query

Language: JavaScript

Web

React Query was created by Tanner Linsley to solve common challenges in managing server state in React apps, such as caching, background updates, and synchronization. It allows developers to focus on building UI while React Query manages the complexity of data fetching and caching efficiently.

React Query is a powerful data-fetching library for React that simplifies fetching, caching, synchronizing, and updating server state in your React applications.

Installation

npm: npm install @tanstack/react-query
yarn: yarn add @tanstack/react-query

Usage

React Query provides hooks like `useQuery`, `useMutation`, and `useInfiniteQuery` to fetch and mutate data. It handles caching, background refetching, pagination, and stale data management automatically.

Fetching data with useQuery

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

function App() {
  const { data, error, isLoading } = useQuery(['todos'], () => axios.get('/api/todos').then(res => res.data));

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error!</div>;

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

Fetches a list of todos from an API and automatically manages loading, error, and cached states.

Mutating data with useMutation

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

function AddTodo() {
  const queryClient = useQueryClient();
  const mutation = useMutation(newTodo => axios.post('/api/todos', newTodo), {
    onSuccess: () => queryClient.invalidateQueries(['todos'])
  });

  return <button onClick={() => mutation.mutate({ title: 'New Todo' })}>Add Todo</button>;
}

Adds a new todo item and invalidates the todos query to refresh cached data.

Paginated queries

const { data, isFetching, fetchNextPage } = useInfiniteQuery(['todos'], fetchTodos, {
  getNextPageParam: lastPage => lastPage.nextCursor
});

Demonstrates fetching data in pages and automatically handling pagination using `useInfiniteQuery`.

Query invalidation

const queryClient = useQueryClient();
queryClient.invalidateQueries(['todos']);

Manually invalidates cached queries to refetch fresh data from the server.

Prefetching data

queryClient.prefetchQuery(['todos'], fetchTodos);

Prefetches data in the background so it is ready when a component mounts.

Error Handling

Network error or failed request: Handle errors using the `error` property returned by `useQuery` or `useMutation` and provide fallback UI.
Stale data shown: Adjust `staleTime` to control when data is considered fresh and triggers background refetch.
Query not updating after mutation: Ensure to invalidate relevant queries using `queryClient.invalidateQueries` after successful mutations.

Best Practices

Use `useQuery` for fetching data and `useMutation` for modifying data.

Leverage query keys for caching and invalidation granularity.

Use `staleTime` and `cacheTime` wisely to balance performance and freshness.

Prefetch data when possible for faster UI transitions.

Combine with `useQueryClient` for advanced cache manipulation and query invalidation.