What this post is and is not
This not a Redux tutorial article. This is about my opinions on why Redux is a good pattern for any front end architecture decisions. My hope is these opinions can be applied anywhere, not just to Redux or React. This article will try to assess each aspect within the context of practical development and iterations. After all, when theory clashes with reality, reality always wins.
Modularity is not the first priority
We were all told that our code should be modular. But the truth is when we are iterating, it’s inevitable that you will have to share state between UI components. Self-contained logic sounds like good idea. But when other components are trying to access information that was previously private, it destroys modularity in a hard way. If the original components were not designed to share their state, the modified code will often be very messy.
Because Redux has a single global store, our brains will tend to think about state in a global sharable way, which leads to components that more easily share state with other components.
One global state container solves real problems
Like I said in previous section, Redux’s single store structure forces us to think about sharing and structuring state at the beginning. Common practices tell us to write modular code. But we should compare this with a database, which is always global most of time.
Redux also forces us to pay more attention to the data structure of stores, which is often overlooked in development. In the end, all apps are just data and how it flows. A store with good data structure is capable of reducing the cost of adding new features and decrease number of bugs.
Flattened structure makes everything easier
People often find adding features to an existing project is hard. The biggest reason is that the related logic is buried deep inside the code base. Think about a function A that calls function B, then function B calls function C. For a new developer to add a feature to function C, there is a high chance they would have to modify function A and function B as well, because A handles arguments and returns from B and B handles C. The same thing can be easily applied to nested UI components.
The action concept in Redux solved this problem very well. Each action is directly accessible within the store (i.e. reducers). We could still do the wrong thing in a Redux action. But we got discouraged since it smells wrong in the context of Redux.
Access to the fine grained event sequence is important
Take AJAX requests as an example. When issuing a request to the server, a typical developer would act when the server returns successfully. This normally means only one action (or event, depending on your framework glossary) would get handled. This level of extraction exposes a big problem for the future. What if we want to, for example, optimistically update the UI? Because we are hiding two items from the sequence ("pending" and "error"), implementing optimistic updates later on will be hard. This level of abstraction looks pretty at the beginning, but it actually make the architecture very inflexible, because it is hiding two types of possible state.
With the Redux action concept, all of those sequences of actions are exposed thanks to middleware like promise-middleware , in which all three statuses of a promise ("pending", "fulfilled", "rejected") are available. We could ignore some of them in our current store implementation, but we still get direct access if we want.
Redux makes us think twice about actions and data structures. But this is not limited to Redux or React. We can improve our use of any framework by following these principles:
- Modularity is not the first priority
- One global state container solves real problems
- Flattened structure makes everything easier
- Access to fine grained event sequence is important