The Hasan Dispatch logo

The Hasan Dispatch

Archives
Log in
Subscribe
September 8, 2020

Redux Toolkit Changed How I Think About State

Classic Redux was 80 lines of boilerplate per feature. RTK was 15. The difference wasn't just code volume — it was a different way of thinking.

#Redux #Redux Toolkit #State Management

The Boilerplate Problem

When I first learned Redux in 2019, the boilerplate was the thing everyone complained about and nobody had a solution for. A single feature — say, fetching and displaying a list of orders — required an action types file, an action creators file, a reducer file, a thunk for the async logic, and a selectors file. That's five files and roughly 80 lines of code to do something that, conceptually, is about 15 lines of logic: call the API, store the result, expose loading and error states. The boilerplate was justified by its advocates as the price of explicit, traceable state management, and there's merit to that argument — every piece of the flow is visible and debuggable. But the cost was real: features took longer to build, the cognitive overhead of navigating five files per feature slowed everything down, and junior developers struggled to contribute because the pattern was intimidating.

Redux Toolkit arrived as the official answer to the boilerplate problem, and the first time I used it, the difference was startling. The same orders feature that took five files and 80 lines in classic Redux took one file and 15 lines in RTK. createSlice combined the action types, action creators, and reducer into a single cohesive unit. createAsyncThunk handled the async lifecycle (pending, fulfilled, rejected) without requiring three separate action types and three separate reducer cases. The result was code that was not only shorter but more readable, because the logic for a single feature was in one place rather than scattered across five files.

A Different Way of Thinking

The reduction in boilerplate wasn't just a convenience — it changed how I thought about state management. With classic Redux, the overhead of adding a new piece of state was high enough that I'd think twice about whether it was "worth" a full slice. That hesitation led to bad patterns: cramming unrelated state into existing slices to avoid creating new ones, or using component state for things that should have been global because the Redux boilerplate felt like too much. RTK lowered the cost enough that the "is this worth a slice?" question stopped being a real consideration, and state ended up in the right place instead of the path-of-least-resistance place.

RTK Query, which came later, took the same philosophy further by eliminating the boilerplate around data fetching entirely. Instead of writing thunks, reducers, and selectors for every API endpoint, you define an API service with a set of endpoints, and RTK Query generates the hooks, handles caching, manages loading states, and invalidates caches on mutations automatically. The first time I used it, I deleted an entire directory of slice files and replaced them with a single API service definition. That experience cemented a principle I've carried into every technology decision since: boilerplate is a tax on productivity, and tools that reduce boilerplate without reducing capability are almost always worth adopting.

When Redux Is Still the Wrong Choice

RTK made Redux dramatically more pleasant to use, but it didn't make Redux the right choice for every application. For applications with genuinely complex client-side state — multi-step forms, real-time collaborative editing, undo/redo — Redux remains an excellent choice because its model of explicit state transitions and time-travel debugging shines in those scenarios. For applications that are mostly server state with a little bit of UI state, Redux is overkill even with RTK, and a lighter solution like TanStack Query plus React context is a better fit. The mistake to avoid is choosing Redux because it's familiar and then using it as a caching layer for server data, which is what most Redux code actually does in practice, and which RTK Query handles better.

The principle I landed on is: use Redux for state that is genuinely global, client-originated, and complex enough to benefit from explicit transitions. Use a data-fetching library (RTK Query or TanStack Query) for server state. Use URL params for state that should survive a refresh or be shareable. Use component state for everything else. Keeping those categories clean is more important than which specific library you use for each one, because the confusion that leads to bad state management usually comes from mixing categories, not from picking the wrong library within a category.

Don't miss what's next. Subscribe to The Hasan Dispatch:
Share this email:
Share on LinkedIn
GitHub
🙃
LinkedIn
Powered by Buttondown, the easiest way to start and grow your newsletter.