React Query v3 (Legacy)
react-query is a robust and widely adopted library for managing, caching, and synchronizing asynchronous data within React applications. This registry entry refers to version 3.x.x of the `react-query` package, with 3.39.3 being the latest patch provided. Since late 2021, active development and new major versions (v4, v5) have transitioned to the `@tanstack/react-query` package as part of a broader monorepo rebrand. Consequently, the `react-query` package (v3) is now in a maintenance-only phase, receiving critical bug fixes but no new features. Its key differentiators include declarative data fetching via hooks (`useQuery`, `useMutation`), automatic background refetching, efficient data caching with configurable stale-time and cache-time, optimistic updates, and a powerful devtools extension. It significantly reduces boilerplate for data management compared to traditional state management solutions for remote data.
Common errors
-
No QueryClient set, use QueryClientProvider to set one
cause The `useQuery` or `useMutation` hook was called in a component that is not a descendant of `QueryClientProvider`.fixWrap your application or the relevant component tree with `QueryClientProvider`, passing an instance of `QueryClient` as the `client` prop. Place it high in the component hierarchy, typically in `App.js` or `index.js`. -
React Hook "useQuery" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.
cause Attempting to call `useQuery` (or any React Hook) outside of a functional React component's top level or a custom hook function.fixEnsure `useQuery` is called directly within a React functional component or a custom hook that itself adheres to the rules of hooks. -
Property 'message' does not exist on type 'unknown'.
cause TypeScript's default error type in catch blocks is `unknown` (since TS 4.4), and React Query's `error` state is typed as `unknown` by default, requiring type narrowing to access properties like `message`.fixNarrow the type of the `error` object before accessing its properties, typically using `if (error instanceof Error)` or a type guard for specific error types like `AxiosError`.
Warnings
- breaking The `react-query` package is superseded by `@tanstack/react-query` for v4 and beyond. Continuing to use `react-query` (v3) means you will not receive new features or active development, only critical bug fixes.
- breaking In `react-query` v3, `QueryCache` was split into `QueryClient`, `QueryCache`, and `MutationCache`. Direct interaction with `QueryCache` and `MutationCache` is less common, with `QueryClient` serving as the primary interface for configuration and interaction.
- gotcha Query functions must return a Promise that either resolves with data or *throws* an error for React Query to correctly handle error states. Libraries like `fetch` do not throw on HTTP error codes by default.
- gotcha When migrating from `react-query` v3 to `@tanstack/react-query` v4, note that `isLoading` for disabled queries behaves differently and might need to be replaced with `isInitialLoading` in specific scenarios. Also, query functions cannot return `undefined` for successful queries in v4.
Install
-
npm install react-query -
yarn add react-query -
pnpm add react-query
Imports
- useQuery
import { useQuery } from '@tanstack/react-query'import { useQuery } from 'react-query' - useMutation
const useMutation = require('react-query').useMutationimport { useMutation } from 'react-query' - QueryClient
import QueryClient from 'react-query'
import { QueryClient } from 'react-query' - QueryClientProvider
import { QueryClientProvider } from 'react-query'
Quickstart
import React from 'react';
import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
// Create a client
const queryClient = new QueryClient();
function Todos() {
const { isLoading, error, data } = useQuery('todos', async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/todos');
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
});
if (isLoading) return <p>Loading todos...</p>;
if (error) return <p>An error occurred: {error.message}</p>;
return (
<div>
<h1>Todos</h1>
<ul>
{data.slice(0, 10).map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
</div>
);
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Todos />
</QueryClientProvider>
);
}