This content originally appeared on DEV Community and was authored by Tianya School
React Query is a powerful state management library designed for handling data fetching, caching, and updating, particularly suited for API interactions. It offers advanced features such as automatic caching, offline state management, data expiration, and refetching.
Installation
npm install react-query
Importing and Configuring React Query
In your application, you need to import the useQuery
Hook and set up a configuration object.
import { useQuery } from 'react-query';
const queryClient = new QueryClient();
Wrap the queryClient
around your root component using QueryClientProvider
to make it available throughout the application.
import { QueryClient, QueryClientProvider } from 'react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
{/* Your application */}
</QueryClientProvider>
);
}
Using the useQuery Hook
Use useQuery
to make API requests and manage response data.
function MyComponent() {
const { data, status, error } = useQuery('myQueryKey', () => fetch('https://api.example.com/data'));
if (status === 'loading') return 'Loading...';
if (error) return 'An error occurred.';
return <div>{data}</div>;
}
Here, myQueryKey
is a unique identifier for the query, and fetch('https://api.example.com/data')
is the actual API call.
Configuration Options
useQuery
accepts a configuration object to set caching strategies, retry logic, etc.
const { data } = useQuery(
'myQueryKey',
() => fetch('https://api.example.com/data'),
{
staleTime: 60000, // Time after which data is considered stale and refetched
retry: 3, // Number of retries
refetchOnWindowFocus: false, // Whether to refetch data on window focus
}
);
Manual Operations
You can manually trigger data refetching, cancellation, or error state setting.
const { refetch, reset, isFetching } = useQuery('myQueryKey', fetchData);
// Refetch data
refetch();
// Clear query state and data
reset();
// Check if data is being fetched
if (isFetching) console.log('Data is being fetched');
Subscribing to Updates
You can subscribe to query state changes using the return values of useQuery
.
const { status, data, error } = useQuery('myQueryKey', fetchData);
useEffect(() => {
if (status === 'success') console.log('Data updated:', data);
}, [status, data]);
Pagination
React Query supports pagination through the useInfiniteQuery
Hook for infinite scrolling.
import { useInfiniteQuery } from 'react-query';
function MyInfiniteList() {
const { data, isFetching, hasNextPage, fetchNextPage } = useInfiniteQuery(
'myInfiniteQuery',
async ({ pageParam = 1 }) => {
const response = await fetch(`https://api.example.com/data?page=${pageParam}`);
return response.json();
},
{
getNextPageParam: (lastPage) => lastPage.nextPageToken || false,
}
);
return (
<div>
{data.pages.map((page, index) => (
<ul key={index}>{page.items.map(item => <li key={item.id}>{item.title}</li>)}</ul>
))}
{hasNextPage && !isFetching && (
<button onClick={fetchNextPage}>Load More</button>
)}
</div>
);
}
Here, getNextPageParam
extracts the identifier for the next page from the previous page’s response.
Cache Updates
When API data is updated, React Query can automatically update the cache, such as during a Mutation
.
import { useMutation } from 'react-query';
const [updateItem] = useMutation(async (updatedItem) => {
await fetch('https://api.example.com/items/' + updatedItem.id, {
method: 'PUT',
body: JSON.stringify(updatedItem),
});
});
// Update data and automatically refresh related queries
updateItem.mutate(updatedItem);
Error Handling
React Query provides built-in error handling mechanisms, allowing you to capture errors via the error
property.
const { data, error } = useQuery('myQueryKey', fetchData);
if (error) return <div>Error: {error.message}</div>;
Query Cache Cleanup
You can clear the cache for specific queries as needed.
queryClient.removeQueries('myQueryKey'); // Clear all matching queries
queryClient.cancelQueries('myQueryKey'); // Cancel matching queries
Custom Middleware
You can extend React Query’s functionality with custom middleware, such as adding logging or performance monitoring.
import { QueryClient, QueryClientProvider } from 'react-query';
const queryClient = new QueryClient({
queryCache: new QueryCache({
middlewares: [
// Custom middleware
myCustomMiddleware,
],
}),
});
Prefetching Data
React Query allows prefetching data before component rendering to improve user experience.
import { usePrefetch } from 'react-query';
function MyComponent() {
const prefetchData = usePrefetch('myQueryKey');
useEffect(() => {
// Prefetch data on component mount
prefetchData();
}, []);
// ...other logic
}
Optimistic Updates
Optimistic updates allow immediate UI updates upon user actions, awaiting server confirmation to provide instant feedback and enhance interaction.
import { useMutation } from 'react-query';
const [updateTodo, { optimisticData }] = useMutation(updateTodoMutation, {
onMutate: (newTodo) => {
// Optimistically update client cache
queryClient.setQueryData(['todos', newTodo.id], newTodo);
return { previousTodo: queryClient.getQueryData(['todos', newTodo.id]) };
},
onError: (err, newTodo, context) => {
// Roll back to previous value on error
queryClient.setQueryData(['todos', newTodo.id], context.previousTodo);
},
onSettled: () => {
// Clean up when query is settled
queryClient.invalidateQueries('todos');
},
});
function handleUpdate(todo) {
updateTodo({ ...todo, completed: !todo.completed });
}
Concurrency Control
Sometimes, you need to control the number of concurrent queries, especially with multiple API calls or high concurrency.
const { data } = useQuery(['concurrentQuery', { limit: 10 }], fetchItems, {
staleTime: Infinity, // Prevent data expiration retries
refetchInterval: false, // Disable auto-polling
refetchOnWindowFocus: false, // Disable refetch on window focus
retry: false, // Do not retry on failure
useErrorBoundary: true, // Use error boundaries to prevent component tree crashes
concurrency: 2, // Maximum concurrent queries
});
Dependency Injection
If your query logic depends on external parameters, you can use useQueries
to execute multiple queries in parallel, each with different configurations.
import { useQueries } from 'react-query';
function MyComponent({ ids }) {
const queries = useQueries(
ids.map(id => ({
queryKey: ['item', id],
queryFn: () => fetchItem(id),
}))
);
return (
<div>
{queries.map(query => (
<div key={query.queryKey[1]}>
{query.isLoading ? 'Loading...' : query.data?.title}
</div>
))}
</div>
);
}
React Query is designed to handle various complex scenarios in modern web applications. With the features above, developers can easily implement data management and state synchronization while maintaining high performance and a great user experience.
👉 Click to join and systematically improve development capabilities: Advanced Development Tutorial
This content originally appeared on DEV Community and was authored by Tianya School

Tianya School | Sciencx (2025-07-21T15:47:07+00:00) React Query Efficient API Request and Cache Management. Retrieved from https://www.scien.cx/2025/07/21/react-query-efficient-api-request-and-cache-management/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.