How to Optimize React Query in React
React Query (now TanStack Query) is an industry-standard library for managing asynchronous state in React applications. While it offers excellent default configurations, scaling your application requires deliberate optimization to prevent redundant network requests, reduce CPU overhead, and eliminate unnecessary component re-renders. This article provides a highly practical guide to optimizing React Query by adjusting cache lifetimes, utilizing selectors, implementing efficient pagination, and fine-tuning global configurations.
1. Adjust staleTime
and gcTime
By default, React Query sets staleTime to
0. This means every time a component mounts or a window
regains focus, React Query marks the data as stale and triggers a
background refetch.
To optimize performance and reduce server load, increase the
staleTime for data that does not change frequently:
const { data } = useQuery({
queryKey: ['userProfile'],
queryFn: fetchUserProfile,
staleTime: 1000 * 60 * 5, // Data remains fresh for 5 minutes
gcTime: 1000 * 60 * 10, // Garbage collect cache after 10 minutes
});staleTime: The duration before data is considered old. Fresh data is always returned from the cache without a network request.gcTime(formerlycacheTime): The duration inactive data remains in the cache before being garbage collected.
2. Prevent Unnecessary Re-renders with Selectors
When a query updates, every component subscribing to that query
re-renders by default. You can optimize this behavior by using the
select option to subscribe to a specific subset of the
fetched data. The component will only re-render if the selected slice of
data changes.
const { data: userEmail } = useQuery({
queryKey: ['user'],
queryFn: fetchUser,
select: (user) => user.email, // Component only re-renders if the email changes
});3. Disable Default Refetching Behaviors
React Query automatically triggers background refetches on window focus, reconnect, or query mount. While useful, these default settings can cause redundant API calls in highly interactive applications. You can disable them globally or on a per-query basis:
const { data } = useQuery({
queryKey: ['staticData'],
queryFn: fetchStaticData,
refetchOnWindowFocus: false, // Disable refetch when user switches tabs
refetchOnMount: false, // Disable refetch when component remounts
refetchOnReconnect: false, // Disable refetch on network reconnect
});4. Implement
Smooth Pagination with placeholderData
When implementing pagination, switching pages normally triggers a
loading state, resulting in a flickering user interface. To optimize the
user experience, use the placeholderData option to keep the
previous page’s data visible while the new page loads.
import { keepPreviousData, useQuery } from '@tanstack/react-query';
const { data, isPlaceholderData } = useQuery({
queryKey: ['projects', page],
queryFn: () => fetchProjects(page),
placeholderData: keepPreviousData, // Keeps old data visible during the fetch
});5. Prefetch Data Before Users Click
Prefetching is a highly effective optimization technique that fetches data before it is rendered on the screen. You can prefetch data when a user hovers over a link, button, or navigation element.
const queryClient = useQueryClient();
const prefetchProjectDetails = async (projectId) => {
await queryClient.prefetchQuery({
queryKey: ['project', projectId],
queryFn: () => fetchProject(projectId),
staleTime: 1000 * 60 * 5,
});
};
// Trigger this function on a button's onMouseEnter event6. Configure Global Defaults
Instead of optimizing each query individually, establish sensible
global defaults when initializing the QueryClient. This
ensures consistent performance optimization across your entire
application codebase.
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 2, // 2 minutes
refetchOnWindowFocus: false,
retry: 1, // Limit retry attempts on failure to reduce network traffic
},
},
});