People often choose Redux before they need it. “What if our app doesn’t scale without it?” Later, developers frown at the indirection Redux introduced to their code. “Why do I have to touch three files to get a simple feature working?” Why indeed!
People blame Redux, React, functional programming, immutability, and many other things for their woes, and I understand them. It is natural to compare Redux to an approach that doesn’t require “boilerplate” code to update the state, and to conclude that Redux is just complicated. In a way it is, and by design so.
Redux offers a tradeoff. It asks you to:
- Describe application state as plain objects and arrays.
- Describe changes in the system as plain objects.
- Describe the logic for handling changes as pure functions.
None of these limitations are required to build an app, with or without React. In fact these are pretty strong constraints, and you should think carefully before adopting them even in parts of your app.
Do you have good reasons for doing so?
These limitations are appealing to me because they help build apps that:
- Persist state to a local storage and then boot up from it, out of the box.
- Pre-fill state on the server, send it to the client in HTML, and boot up from it, out of the box.
- Serialize user actions and attach them, together with a state snapshot, to automated bug reports, so that the product developers can replay them to reproduce the errors.
- Pass action objects over the network to implement collaborative environments without dramatic changes to how the code is written.
- Maintain an undo history or implement optimistic mutations without dramatic changes to how the code is written.
- Travel between the state history in development, and re-evaluate the current state from the action history when the code changes, a la TDD.
- Provide full inspection and control capabilities to the development tooling so that product developers can build custom tools for their apps.
- Provide alternative UIs while reusing most of the business logic.
However, if you’re just learning React, don’t make Redux your first choice.
Instead learn to think in React. Come back to Redux if you find a real need for it, or if you want to try something new. But approach it with caution, just like you do with any highly opinionated tool.
Finally, don’t forget that you can apply ideas from Redux without using Redux. For example, consider a React component with local state:
It is perfectly fine as it is. Seriously, it bears repeating.
Local state is fine.
The tradeoff that Redux offers is to add indirection to decouple “what happened” from “how things change”.
Is it always a good thing to do? No. It’s a tradeoff.
For example, we can extract a reducer from our component:
Notice how we just used Redux without running npm install. Wow!
Should you do this to your stateful components? Probably not. That is, not unless you have a plan to benefit from this additional indirection. Having a plan is, in the parlance of our times, the 🔑.
Redux library itself is only a set of helpers to “mount” reducers to a single global store object. You can use as little, or as much of Redux, as you like.
But if you trade something off, make sure you get something in return.