Skip to content

Why are we still overcomplicating state management in React?

The same pattern keeps showing up in different React apps: forcing API data into Redux, shoving user profiles into Context, or manually building fetching logic with `useEffect`.

2 min read
TanStack Query
Server State
Caching
React
Invalidation

The same pattern keeps showing up in different React apps: forcing API data into Redux, shoving user profiles into Context, or manually building fetching logic with useEffect. The real issue isn't the tools we choose, but how we misidentify the data we're actually handling.

Too many developers treat server state as if it were client state. They couldn't be more different.

Client state is local and temporary: things like whether a modal is toggled open or the text currently sitting in a form draft. Server state belongs to the server, like a list of invoices or a user profile. When you dump server data into a client-side global store, you're basically building a time bomb of stale data and inefficient fetching.

This is where TanStack Query comes in. It’s more than just a data-fetching library; it’s a caching system that draws a clear boundary between remote data and your own local application data.

The concept of query keys is crucial here. Using a hierarchy, for example, ['invoices'] and ['invoices', 1], lets you invalidate an entire domain of data systematically without getting lost in a mess of strings.

When managing a data lifecycle, you really only need to focus on four things: how you fetch, how you manage cache expiration via staleTime, how you invalidate data after a mutation, and how you reuse existing data. With this pattern, you don't have to keep showing empty loading spinners every time a user switches pages; you can keep the old data visible while the refresh happens in the background.

Of course, it has its downsides too if you aren't careful. You have to be precise. Don't pass unstable objects into your query key, or you'll trigger infinite fetches. And never ignore error states or empty data just to keep your code "clean."

Not every app needs TanStack Query. If your app relies heavily on server-side loaders, like in Remix or Next.js, and has very little need to share data on the client, you might be fine without it. But the moment syncing data between pages starts feeling like a struggle, that's your signal to switch.

The core idea is simple: separate what belongs in local memory from what is just a reflection of what's on the server.