Avoid Redux in your Enterprise-Level Web Applications
Redux is awesome, don’t get me wrong. But it has its pros and cons. I worked with Redux for many years and I also had the chance to try out other state-management libraries. But most of them failed in different areas. Here are a few things you should consider.
“What on earth is Redux I keep hearing about?”
In a nutshell, Redux can be considered as a central store that manages the state of an application built in React, Angular, or other popular frameworks. Each of its components can access the given stored state without requiring sending down property from one component to another.
Here is a summary of Redux’s architecture:

- Reducer: Logic that decides how your data changes exist in pure functions
- Centralized store: Holds a state object that denotes the state of the entire app
It is a great library to learn though, it will build up your skills and knowledge and teach you about many useful concepts when it comes to software development.
“So why should I avoid it in my next enterprise-level web application?”
1. It can be a steep learning curve
If you’re starting out with Redux, it’s probably not a great idea to build a scalable & enterprise-level ready application. This is because you probably don’t have the right skills to build one. Such an application requires a well-thought structure to allow maintainability and scalability.
2. There are not enough advanced teaching content
Most of the tutorials and courses you’ll find online will showcase a simple Redux application. Some of them will teach you how to build a TODO app, which is fine, but not enough to give you the right skills to build the next award-winning or interprise-level web application.
Sure, some paid courses would teach you about handling asynchronous calls for example, but so far I haven’t seen a proper course that teaches you how a senior front-end engineer should build an enterprise-level application (I mean with proper structure, best practices, normalization, handling async requests, testing & TypeScript).
3. Too much boilerplate, repetition, and restricted design
Let’s face it. When adding redux, you have to write a bunch of code. Think about all the types, actions, operations, reducers, and tests you have to write! This, without mentioning all the custom middlewares you have to add and manage. Have you thought about all the loading, success & error states you have to manage in each asynchronous action? Sure, you can wrap that logic in a separate UI reducer and make custom selectors. What about all the components where you have to import useDispatch
and useSelector
? At a certain point, it becomes really boring, hard to manage, and not fun to code anymore (see next point).
4. Poor Developer Experience & things can quickly get messy
There are a few common use-cases where things can get messy. I will just name a few.
- Working with other developers can be a pain point. Some of them might prefer everything in a single file (types, actions, reducers) — ducks pattern. Others might prefer multiple files, even when the “feature” is small and probably doesn’t require 6 files (like
actionTypes.js
,actions.js
,reducers.js
,operations.js
,selectors.js
,index.js
). - Although it’s important to separate concerns, sometimes it can be hard to manage interconnected features.
- Working with async code requires a lot of boilerplate or even the need to use middlewares (ie:
redux-thunk
,react-redux-promise
). Without middlewares, things can get even messier, with action types like.
const FETCH_USERS_REQUEST = “FETCH_USER_REQUEST”;
const FETCH_USERS_SUCCESS = “FETCH_USER_SUCCESS”;
const FETCH_USERS_ERROR = “FETCH_USER_ERROR”;
Imagine you have 50 different action types like these!
6. Hard to test
Ok, this is not about which library you should use to test your react applications, but let’s say you want to test the internals of a component that is connected to the Redux store using Jest/Enzyme. You have to wrap your component with a custom Redux provider and add the appropriate initial values. I find that sometimes it’s not ideal as you might end up with endless errors.
7. Typescript Refactoring Hell
Have you ever tried to refactor an existing Redux application into Typescript? I just sincerely wish you Good luck! 🤞
8. Security issues
No encapsulation. Any component in your application can access the data which can cause security issues.
9. Performance issues
As state is immutable in redux, the reducer updates the state by returning a new state every time which can cause excessive use of memory.
“What can I use then?”
For simple states
- You can use React Hooks to create your own state management custom hooks. There are plenty of tutorials and courses about this.
- The React Context API could be also an alternative.
For more complex states
- Have a look at React Query (personal preference), which is a revolution in my opinion. This works for both REST APIs as well as GraphQL.
Fetch, cache and update data in your React and React Native applications all without touching any “global state”.
Instead of writing reducers, caching logic, timers, retry logic, complex async/await scripting (I could keep going…), you literally write a tiny fraction of the code you normally would. You will be surprised at how little code you’re writing or how much code you’re deleting when you use React Query.
- Another alternative is SWR (more appropriate for JAMSTACK applications).
- If your backend supports GraphQL, you can also go for Apollo Client.
As always, happy coding! And don’t hesitate to share your thoughts about this article in the comments section 🙌