Skip to main content

Command Palette

Search for a command to run...

React Query vs useEffect for API Calls, Which One Should You Us

Updated
5 min readView as Markdown
React Query vs useEffect for API Calls, Which One Should You Us
H
I'm a software developer with 5 years of experience building web and cross-platform applications using the MERN stack, React Native, and Electron.js. I love turning ideas into fast, scalable, and user-friendly products from web apps to mobile and desktop solutions. I write about JavaScript, React, and my journey building real-world applications. Always open to connecting with fellow developers, collaborating on interesting projects, or just talking tech!

If you have spent any time building React apps, you have definitely written this before.

useEffect(() => { fetchData(); }, []);

It's the classic way to fetch data when a component loads. But as apps grow, this pattern starts showing its cracks, specially around performance and how many times your backend gets hit for no reason.

I've worked as a Mern stack developer for a while now and i used to rely on useEffect for basically every api call. After actually spending time with Tanstack React Query on real projects, i realized how much unnecessary work useEffect was creating, both for me and for the backend.

Lets go through the reasons.

The Double Call Issue

One of the first things devs notice is their api getting called twice when the app loads. This happens because of React StrictMode which intentionally double invokes effects in development to help catch bugs. Fine in theory, but it also exposes a real problem, useEffect fires again every time a component mounts, with no built in way to know if the data is already fresh.

In apps with tabs, modals, or components that mount and unmount often, this adds up to a lot of repeated api calls hitting your backend unnecessarily.

React Query solves this with built in caching. Once data for a query key is fetched and still considered fresh, it wont call the api again, it simply returns what's already cached.

Too Much Manual State Management

Fetching with useEffect also means you're manually handling loading, error, and data state every single time.

jsx const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null);

Now imagine writing this pattern for every api call across a mid to large size app. Its repetitive and it opens the door for small bugs, like forgetting to update loading state properly in the catch block.

React Query removes all that boilerplate. You get isLoading, isError, data and error straight from the hook, no extra state management needed.

Dependency Arrays Are a Trap

If you have used useEffect long enough you know dependency arrays can be tricky. Miss one dependency and you get stale values inside your effect. Add too many and your effect starts running way more often then it should, sometimes causing infinite loops if you're not careful.

React Query avoids this entirely. You define a query key and a fetch function, and it decides when to refetch, on mount, on window focus, or based on staleTime, without you managing an array of dependencies by hand.

Cache, StaleTime and Invalidate Queries

This part is honestly what makes React Query worth switching to.

Cache stores your fetched data by query key, so navigating back to a page you already visited shows instant data instead of a loading spinner every time.

staleTime decides how long data stays "fresh" before react query considers it old. While data is fresh, it skips unnecessary refetching, which means less pressure on your backend.

invalidateQueries is used after mutations. Say you update or delete something, you just invalidate that query key and react query automatically refetches the correct data. No manual re fetching logic needed after every mutation.

Together these three features remove most of the manual work that useEffect based fetching required.

Example From a Recent Project

On a Next js SaaS platform i built recently, the dashboard was using useEffect for almost every section, and it was calling the backend repeatedly every time users switched tabs. This was clearly not scalable and users were noticing slower load times.

I moved everything to React Query, set proper staleTime values, and used invalidateQueries for all mutation related updates. The dashboard became noticeably faster and backend calls dropped a lot. If you want to check that out, here is the Next.js SaaS Platform i built for managing mineral owner contact data.

Does This Mean useEffect Is Bad

No, useEffect is still needed for things outside data fetching, like event listeners, subscriptions, or syncing with browser apis. It's just not the best tool anymore for fetching data from an api, specially when React Query exists and handles it in a much more optimized way.

Final Thoughts

Working as a Mern stack developer i have seen firsthand how much unnecessary backend load comes from apps still using plain useEffect for fetching. Moving to React Query fixed most of those issues in projects i worked on, and gave built in caching and invalidation without extra effort.

If you are building react or next js apps and havent tried react query yet, i would suggest starting with just one page and comparing the difference yourself. As a Full stack Mern & Next.js developer this is one of the first things i recommend when reviewing a project's api layer.

Would love to hear your experience if you already switched, or if you're still team useEffect for now.