
In the year of our Lord 2026, I still see react devs manually setting up use state hooks for storing fetched data. Along with a use effect hook for actually triggering the fetch, and don't forget about error handling and loading states 🫠.
React Query has made client side fetching and caching a breeze! And I'll share with you a few reasons why I love using it. and how to get started with it.
Reducing State Management
As I mentioned above, here is an example of the code the uninitiated would write.
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetch('https://api.example.com/data').then((response) => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
}).then((data) => {
setData(data);
setLoading(false);
}).catch((error) => {
setError(error);
setLoading(false);
});
}, []);
It's not the worst thing in the world, but what if you need multiple of these for a single component? Also it's just a pain to handle, and not to mention one more imporant fact.. This doesn't even include the benefits of caching.
But before I get into that, here is a simple example of using React Query to accomplish the same thing seen above.
const { data, isLoading, error } = useQuery({
queryKey: ['data'],
queryFn: () => fetch('https://api.example.com/data').then((res) => res.json()),
});
🤯🤯🤯
Even if this was all React Query did, it would be still be an ergonomic solution. But it does so much more.
Caching
While working on a large project you may have multiple components that need to fetch the same data. Or maybe you have a heavy network request that you don't need refetched every time a component remounts. Well React query has you covered.
const { data, isLoading, error } = useQuery({
queryKey: ['my-heavy-network-request'],
queryFn: () => fetch(...),
staleTime: 1000 * 60 * 5, // 5 minutes
});
By default, a fetch is considered stale immediately after it is fetched. But we can change that by setting the staleTime option.
Now if the component is mounted again, the data won't be need refetched if 5 minutes have not passed.
Where this becomes powerful in an app making duplicate requests is when you use the same queryKey and function definition, React Query will know its the same call and use the internal cache.
Meaning, no more prop drilling endlessly. Typically you would have to make sure a parent component makes the data fetch and then pass that data down to multiple child components. But with React Query, components can be much more modular and reusable by handling the data fetching themselves without the cost of redundant network requests!
To be sure I use the same key and function definition, I'll typically use a custom hook to handle the query.
export const useMyHeavyNetworkRequest = () => {
return useQuery({
queryKey: ['my-heavy-network-request'],
queryFn: () => fetch(...),
});
};
Now I can use this hook in any component I need to fetch that data.
Conclusion
React Query is a powerful tool that can help you manage data fetching in your React applications. It's a great way to handle caching, error handling, and loading states. It's a great way to make your life easier as a developer.
I hope this blog post has helped you understand why I love using React Query and how to get started with it.