React Query is a data-fetching library that helps with fetching, caching, synchronising, and updating the server state in your react application. Before we start going into the details of React Query, it’s essential to understand the server state.
Note: This article requires an intermediate understanding of building client-side applications with React.
Most developers with experience in React understand what state is. To understand server state, let’s list the differences between client & server state.
Client State
- Data is stored in the browser.
- The user can change the data locally.
- Data lives in memory and is lost on reloads.
Server State
- Data is stored on a remote server.
- Changing data requires remote server access or APIs.
- Data persists on a remote database.
React Query only helps manage the server state.
What can React Query help with?
1. Caching
React Query sits between your server and the client intercepting every query. Each query is tied to a unique key called the query key in the react query store. When you try refetching data for the same query key, it immediately returns the cached data.
2. Identifying and updating stale data in the background
When data is fetched from the server, it compares it to cached data. If both are the same, it doesn’t force a reload. React Query also ensures the cache is updated by making fetch requests in the background to keep data in sync.
3. Memory management and garbage collection
React Query has a garbage collector for managing the cache in the browser. If the data in the cache is not consumed, it gets deleted after a timeout. This timeout can be configured locally for each query or globally. This helps retain only the most relevant information while clearing the rest from the cache.
4. Deduping multiple requests for the same data
If multiple requests for the same query key are made close to each other, only the first one will be sent over the wire. All the other requests will be resolved with the data from the first one. This saves bandwidth and improves the user’s experience.
5. Performance optimisations
React Query has out-of-the-box support for render optimisations, pagination, lazy loading, etc., to improve your application's performance.
When should you use React Query?
React Query is a tool. To get its maximum benefit, it’s essential to know when to use it. Let’s understand this with two examples
- A social media application: Users can view timelines/posts and chat with friends. The usual.
- A music production application: An offline first application where users can create music with different instruments.
React Query would be better suited for the social media application. This application would naturally integrate with several APIs thus requiring server state management. This application would also have its client state, so a library like React Query will allow you to manage the server state well.
React Query has become popular because most applications have some or the other form of the server component. As the number of APIs increases, it makes more sense to use this library.
Here are a few pointers on why React Query is a worthy choice:
- Simplifies data fetching - Erases a lot of boilerplate and makes your code easy to read.
- Provides tools to improve the reliability of server data - Invalidates and marks data as stale.
- Provides tools to improve User Experience - Prefetching, re-fetching, caching, and more.
- Performance optimisations - Pagination and lazy loading.
- Request Retries - Ability to retry in case of errors.
- Window Focus Refetching - Refetching based on application tab activity.
Does React Query replace Redux, MobX or other global state management libraries?
TL;DR no. Redux, MobX, and the other popular libraries are great for client state management. React Query complements these libraries and helps create a separation of concerns. In most client-side applications, they deal with the server state. They typically perform these steps:
- Fetch data from the server
- Store the server data in the client store
- Provide a medium for components to communicate and access the data from the store.
React Query helps you abstract the above functions. Hence only leaving the actual client state to be stored using client state management libraries. This is why you’re left with the very little state to maintain when you migrate to React Query.
Pitfalls when working with React Query
1. Large Bundle Size
React Query has an impact on application size. It is because of all the features that comes with it.Large bundle size could impact your performance. It could cause delays during load, render and user interaction. To give more context, according to BundlePhobia React Query is about 3 times larger than one of its competitor SWR . The idea here is not to scare you for its bundle size, instead to help you check if it's a perfect fit for your application.
React Query bundle size as reported by bundle phobia.
SWR bundle size as reported by bundle phobia.
2. Identifying Query Keys to Invalidate
React Query provides a useMutation hook to update data on your server. If the update to the server goes through, It provides callback function called onSuccess and onMutate to update the cache in the react query store. The pitfall here is that for every update made to the sever, a manual step is required to identify and invalidate the query keys. Let’s understand why this gets tricky:
- Identifying all the query keys in the React Query store related to the update that went through is difficult. There is a high potential that you could miss out on identifying these keys in large applications. It is error prone and a good understanding of the platform is required to avoid this pitfall.
- The query keys are usually invalidated with the help of invalidateQueries call. The query keys that you pass into the invalidateQueries call should match with the query key that was initially set. If your query key is an array of Strings, the order in which the parameters are passed in the query key should also be the same.
3. Identifying Query Keys to Cache
React Query requires appropriate query keys for caching to work as expected. Setting them can get tricky at times. To understand better, let’s consider an example. Let’s say you need to fetch the count of all the users that came to your platform from start of the year. The query to fetch the users count is dependent on the start date and the end date. These parameters should be part of the query key as shown below
[
'usersCount',
{ startDate: '2022-01-01+00:00:00', end_date: '2022-12-08+00:53:56' }
];
Let’s say I came after a minute and requested for the users count from start of the year, it doesn’t cache. It instead requests directly from the server. This shouldn’t be the expected behaviour. Ideally with all that we have learnt, it should have returned from the cache while fetching for the latest updates in the background.To understand this, lets structure the query key for the request that was made after a minute.
[
'usersCount',
{ startDate: '2022-01-01+00:00:00', end_date: '2022-12-08+00:54:56' }
];
You would notice that the end date has changed. This key is not matching with the old one that is present in the cache. So it fetches directly from the server.
To fix this issue, the parameters set in the query key needs to be modified. Instead of considering the entire timestamp what if we just consider the date in the query key. It is important to note that the parameters that are passed to the API call are still unchanged. We still send the entire timestamp. It is only in the query key that the modification is made as shown below.
[
'usersCount',
{ startDate: '2022-01-01', end_date: '2022-12-08' }
];
If you came back after a minute and made a request, it would instantly fetch from the cache. This is because your query key is unchanged and matches with the one in the cache. But what’s interesting here is that the background fetch is for the requested timestamps from the server. Once the latest updates are available, it paints it on to the users count section.
Data Fetching in React Query
Data fetching is a very common side effect that is usually managed with useEffect. It has become way simpler with React Query.
To understand better, let’s compare how data fetching is implemented in useEffect and React Query. We’ll use axios and JSONPlaceHolder API to fetch the posts.
// Fetch all posts
const fetchPosts = async () => {
const { data } = await axios.get(
"https://jsonplaceholder.typicode.com/posts"
);
return data;
};
Using useEffect:
const [isLoading, setIsLoading] = useState(false);
const [data, setData] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
setIsLoading(true);
try {
(async () => {
const data = await fetchPosts();
setData(data);
})();
} catch (error) {
setError(error);
} finally {
setIsLoading(false);
}
}, []);
Using React Query:
const { isLoading, data, error } = useQuery("posts", fetchPosts);
React Query provides a useQuery hook to fetch data from the server. The entire useEffect implementation was replaced with a single line in React Query. It erased a lot of boilerplate and makes your code easy to read. The loading, error and your data state is handled out of the box. In addition to this, it also helps with caching, background-sync and a bunch of other things.
Conclusion
React Query is a fantastic library. In most cases, it removes the need for your global state managers. It also helps you erase a lot of boilerplate and makes it easy to use in large applications. In addition, it handles caching, background updates, request retries, performance optimisations, and other things.
Before you start integrating this library in full swing, you must carefully examine if this is something your application needs. If you want to use it for its essential functionalities, you must consider other lightweight alternatives.
Overall, it's an absolute winner, and there is nothing close to the features it offers. No doubt, it is a missing data-fetching library for React.
Thanks for reading. This article was originally posted on the Wednesday Solutions blog. You can check out the original article here.
Top comments (0)