Why I Abandoned Data-Fetching Hooks for Redux in 2026
I spent two years avoiding boilerplate, only to realize I was just creating chaos.


People said Redux was dead. They said data-fetching hooks were the future because they eliminated boilerplate. I ran with it for a couple of years. That's until my application's complexity exploded, and I realized that the "easy" or "convenient" way is not necessarily the best way.
I started using Redux when it was trending, back when we wrote switch statements by hand and argued about thunks versus sagas. When data-fetching hooks (like React Query and SWR) became popular, I jumped ship quite quickly. The promise was seductive:
- Less boilerplate: No more actions, reducers, or selectors. Just a hook call.
- Local state: Stop worrying about global state; just fetch data where you need it.
- Magic caching: Automatic deduplication and re-fetching behind the scenes.
For a couple of years, I ignored the downsides. It felt like I had escaped the complexity trap. But as I built Viduli, a platform with deep, interactive entity relationships, I hit a breaking point.
The chaos of the "easy" solution
The problem with the data fetching abstractions is that they work beautifully until you need to do something slightly more complex than just fetching data.
In Viduli, we have complex application-side logic. Users manipulate projects, teams, and deployments in real-time. This isn't just displaying a list; it's heavy interaction.
The problems started appearing with client-side mutations. When a user updated an entity, I had to manually update the cache to reflect that change instantly. But because the cache key logic was decentralized (scattered across dozens of components), I found myself searching through code just to find where the data was stored.
It felt hacky. The cache wasn't normalized. If I fetched a list of projects in one query, recent projects in another and a single project details separately, updating one didn't automatically update the other. I had to manually invalidate or update multiple cache keys.
My codebase turned into a game of: "Did I already fetch this somewhere else?" "What was the exact key I used?" "Oh wait, those two queries are similar but use different keys. Time to refactor both."
It was chaos. I had traded the "boilerplate" of Redux for the spaghetti code of scattered side effects.
The return to structure
Redux, for all its verbosity, gives you one thing hooks never could: Structure.
It forces you to separate your data from your UI. It gives you a single source of truth. When I switched Viduli back to Redux, the mental fog lifted.
I set up a centralized state store with normalized entities using Redux Toolkit.
- No more duplicate data: A project exists in one place in the store, regardless of how many views display it.
- Predictable updates: Dispatch an action (
updateProject), and every component using that project updates automatically. - Clear mental model: Actions describe what happens, reducers describe how state changes.
I was no longer hunting for cache keys in random hooks. I was simply selecting data from a central tree structure. It made intuitive sense again.
But what about the boilerplate?
"But Redux takes so much code!" used to be a valid complaint. In 2026, it's not.
First, Redux Toolkit (RTK) cut the boilerplate by half. You write slices, not switch statements. Second, LLMs have made the argument irrelevant. I don't type boilerplate anymore; I describe the state shape, and the AI generates the slice, actions, and selectors instantly.
The only thing I missed from the hook libraries was the automatic polling and re-fetching. So, I wrote my own hook.
It took less than 80 lines of code.
It does three things:
- Re-fetches data when dependencies change.
- Re-fetches data when the browser tab is focused.
- Re-fetches data on a fixed interval (polling).
All three features are optional and toggleable. That's it. I didn't need a heavy third-party library to handle 90% of my data-fetching needs.
Here is the gist of it:
import { useEffect, useRef } from "react"
export function useRefresh(
action: () => void,
options: {
interval?: number
onFocus?: boolean
dependencies?: any[]
} = {},
) {
const { interval, onFocus = true, dependencies = [] } = options
const actionRef = useRef(action)
actionRef.current = action
// Handle focus re-fetching
useEffect(() => {
if (!onFocus) return
const onVisibilityChange = () => {
if (document.visibilityState === "visible") {
actionRef.current()
}
}
window.addEventListener("visibilitychange", onVisibilityChange)
return () => {
window.removeEventListener("visibilitychange", onVisibilityChange)
}
}, [onFocus])
// Handle interval polling
useEffect(() => {
if (!interval) return
const id = setInterval(() => actionRef.current(), interval)
return () => clearInterval(id)
}, [interval])
// Handle dependency changes
useEffect(() => {
actionRef.current()
}, [...dependencies])
}And using it is as simple as:
// In your component
const dispatch = useAppDispatch()
const projectId = "proj_123"
useRefresh(() => dispatch(fetchProjectDetails(projectId)), {
interval: 30000, // Poll every 30s
onFocus: true, // Refetch when tab is focused
dependencies: [projectId],
})Conclusion
Tools like React Query are fantastic for simpler applications or when you truly just need a server-state cache. But for heavy, interactive applications where entities are mutated and shared across many views, Redux wins.
It’s not about following trends. It’s about choosing the right architecture for your complexity. I’m happy to be back in the land of actions and reducers where I can control exactly where my data lives and how it mutates.
About Viduli
If you read this far, you may be wondering what Viduli is. Viduli is a platform to deploy your full stack applications without the usual hassle associated with big clouds.
If you've deployed frontends to Vercel, you know how convenient it is. Viduli does the same for your frontend, backend, and database.
If you want to see what a structured, high-performance management console feels like, give it a try.
Deploy your first project for free on Viduli → https://viduli.io/sign-up
Join our newsletter
Get the latest updates and insights delivered to your inbox.