Published: 6 November 2023
Introduction
As React applications grow more complex, managing data fetching, caching, and state management becomes increasingly difficult. In the past, developers often relied on useEffect to manage API calls, but this approach can become cumbersome as your app scales. This is where React Query (now known as TanStack Query) comes into play.
React Query is a data-fetching and caching library for React that simplifies fetching, caching, synchronization, and more. It abstracts the complexities of data management while providing developers with a powerful set of features that reduce boilerplate code, enhance performance, and improve the overall developer experience.
In this post, we’ll explore why React Query is the best way to fetch data in React applications. We’ll dive into its key features, including automatic caching, background syncing, and how it handles complex data patterns such as pagination and infinite scrolling.
What is React Query (TanStack Query)?
React Query (TanStack Query) is a library that abstracts and simplifies the process of handling server state in React applications. It provides a set of utilities to fetch, cache, sync, and manage server-side data in a declarative and efficient way.
React Query does a lot of the heavy lifting for you, including caching responses, refetching data when necessary, managing loading and error states, and even supporting background updates. It allows you to focus on your application’s logic rather than worrying about handling API calls and caching.
Key Features of React Query:
- Automatic Caching: React Query caches API responses to avoid unnecessary network requests and speed up re-renders.
- Background Fetching: React Query can fetch data in the background when the component re-renders, ensuring the user is always viewing the most up-to-date information.
- Built-in Pagination & Infinite Query Support: React Query offers built-in support for handling pagination and infinite scrolling with minimal effort.
- Automatic Refetching: React Query automatically refetches data based on certain events like window focus, network reconnect, or interval timing.
- Data Synchronization: React Query automatically synchronizes your data with the server, so the UI always reflects the most recent changes.
How React Query Simplifies API Calls and Caching
One of the most significant advantages of React Query is its ability to simplify data fetching and caching. Let’s look at how React Query improves the process.
1. Simplified API Calls
React Query abstracts the logic around fetching data. With traditional methods, you might have to manage loading states, error handling, and data manipulation inside useEffect. With React Query, much of this is handled automatically.
Here’s an example of a simple data fetch with React Query:
import { useQuery } from 'react-query';
const fetchData = async () => {
const response = await fetch('/api/data');
return response.json();
};
const DataComponent = () => {
const { data, error, isLoading } = useQuery('data', fetchData);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>{JSON.stringify(data)}</div>;
};
In this example, React Query automatically handles:
- Loading and error states with the
isLoadinganderrorproperties. - Caching of the fetched data, so subsequent renders won’t re-fetch the same data unnecessarily.
- Automatic refetching when the window is focused or network status changes (optional but configurable).
2. Automatic Caching
React Query caches the result of a query by default. This means that if you make the same request again, React Query will return the cached response immediately without needing to hit the network, reducing unnecessary requests and improving performance.
For instance, after the first call to the fetchData function, React Query will store the data in memory. If you request the same data again, it will serve the cached data instead of performing another network request.
Real-World Examples with Pagination and Infinite Scrolling
Now that we’ve seen how React Query simplifies data fetching and caching, let’s take a closer look at how it handles complex use cases such as pagination and infinite scrolling.
1. Pagination with React Query
Pagination is a common requirement for data-heavy applications (e.g., blogs, product listings, and user dashboards). React Query simplifies pagination by allowing you to easily manage the current page and fetch the relevant data as needed.
Here’s an example of how to implement pagination with React Query:
import { useQuery } from 'react-query';
const fetchPaginatedData = async (page) => {
const response = await fetch(`/api/data?page=${page}`);
return response.json();
};
const PaginatedComponent = () => {
const [page, setPage] = React.useState(1);
const { data, error, isLoading } = useQuery(['data', page], () => fetchPaginatedData(page), {
keepPreviousData: true, // Keeps old data visible while new data is loading
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<div>{JSON.stringify(data)}</div>
<button onClick={() => setPage((prev) => prev - 1)} disabled={page === 1}>
Previous
</button>
<button onClick={() => setPage((prev) => prev + 1)} disabled={!data.hasNextPage}>
Next
</button>
</div>
);
};
How it works:
- We fetch paginated data based on the
pageparameter. - The
useQueryhook uses thepageas a dependency and automatically refetches data when the page changes. - The
keepPreviousDataoption ensures that the old data stays visible until the new data arrives, preventing the UI from flashing.
2. Infinite Scrolling with React Query
Infinite scrolling is another common requirement, particularly for social media apps, product lists, or chat apps where the user may continuously scroll down for more content.
React Query has built-in support for infinite queries, which allow you to fetch additional data as the user scrolls.
Here’s an example of implementing infinite scrolling:
import { useInfiniteQuery } from 'react-query';
const fetchInfiniteData = async ({ pageParam = 1 }) => {
const response = await fetch(`/api/data?page=${pageParam}`);
return response.json();
};
const InfiniteScrollComponent = () => {
const { data, error, isLoading, fetchNextPage, hasNextPage } = useInfiniteQuery(
'data',
fetchInfiniteData,
{
getNextPageParam: (lastPage) => lastPage.nextPage, // Determine next page based on the data
}
);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
{data.pages.map((page, index) => (
<div key={index}>{JSON.stringify(page)}</div>
))}
<button onClick={() => fetchNextPage()} disabled={!hasNextPage}>
Load More
</button>
</div>
);
};
How it works:
- The
useInfiniteQueryhook is used to handle data fetching for infinite scroll. getNextPageParamdetermines the next page based on the data (e.g., the API response includes anextPageparameter).- The
fetchNextPagefunction is called to fetch the next page of data when the user clicks the “Load More” button.
Conclusion
React Query (TanStack Query) is an invaluable tool for simplifying data fetching, caching, and state management in React applications. It reduces boilerplate code, enhances performance, and makes complex data handling like pagination and infinite scrolling much easier.
By using React Query, you can:
- Automatically handle caching and background syncing.
- Simplify pagination and infinite scrolling with built-in hooks.
- Focus on building your application logic instead of dealing with API requests and error handling.
In a modern React application, React Query is an essential library for fetching, caching, and syncing data in a declarative and efficient way. Whether you’re building a small app or a large-scale data-driven platform, React Query should be your go-to tool for managing server state.