As soon as you get into writing a React web application, you start to feel the need for a global state management mechanism. This is where state management solutions like Redux and MobX come into play.
Or is it really?
This may have been the case centuries ago, but not anymore. Before going into the details, let’s have a quick look at Redux.
The One True King
During the Flux wars, when Dan Abramov introduced Redux during his Hot Reloading talk, two things happened: the audience clapped, and then, the great flux war ended — crowning Redux as the One True King !!!
The killer features revealed in that talk were its simplicity over Flux and Time Traveling. Redux also came with middleware, which made things like logging, audit trails and alerting easier.
You want data-fetching and side effects? Cool, here’s Redux-Thunk, or Redux-Saga.
You want to manage a Form? Cool, here’s Redux-Form.
You want to sync route changes with Redux? Cool, here’s React-Router-Redux.
I can go on, and on…
React + Redux
In my opinion, React was initially built with the Unix philosophy in mind.
React delivers the foundation for declarative UI implementation, and the community builds libraries and tools to suit different needs and flavours. React did not deliver a clear state management solution on its own (for example, similar to how Vue has Vuex). I do think that following the Unix philosophy has done more good than harm to the growth of React. However the current state of affairs around state management is a bit more complicated than that.
When you actually think about it, handling component state with React wasn’t the hard part — local state did that well. Sharing state between components was slightly harder. But the hardest thing of all to handle was side effects.
How would you call an API endpoint and render a UI based on it?
The idiomatic approach was to set the initial state in the constructor, call the endpoint in the componentDidMount lifecycle method and set local state (using this.setState) there. Finally, based on the local state, you would render the component. Things get harder when you want to re-fetch the data whenever the component is updated, since you need to implement the componentDidUpdate lifecycle method as well. Things get even harder still, when sharing the fetched data with the state of another component. Thus, it made perfect sense to use Redux or MobX as the skeleton of a React application.
So why not Redux?
Redux did fill this gap, but it also introduced a lot of indirection to an app.
For instance, to achieve the same thing mentioned before, we need:
- A reducer
- An action type
- An action creator
- A map state function
- A thunk based dispatch (Or a Redux Saga) and
- A Container to bind the state mapper and dispatcher to a Component
Too much crap to do a simple API call.
What if we wanted to show a spinner while data is being fetched? Then we would need to add a new property to the reducer state. On top of them, we need to write tests. Each new module we write potentially requires a test for it as well.
Imagine doing this for every API call… !!!
Because of these complications, a React app built using Redux became more like a Redux app built using React.
The community came up with different approaches to make things simpler. I myself, built a small framework around Redux and Redux Saga to make fetch calls simpler by enforcing a set of solid conventions. It had only one action type for all fetch calls and a key was passed with the action to uniquely identify the fetch call. This enabled me to use a single Saga and a single reducer to handle all my fetch calls. Handling fetch statuses became easier because of this single reducer.
I’ve seen similar efforts to solve such indirections introduced by Redux. One popular example is ducks(https://github.com/erikras/ducks-modular-redux), for defining the reducers, action types and actions in one place.
But then I started thinking…
What is the point of doing Redux by not doing Redux?
Fast forward to today. React has changed a lot — in a good way.
- The new context API has arrived.
- Hooks are all over the place.
- Suspense for data fetching is on the way.
Now these goodies can be utilised to replace all the bloat and indirection introduced by Redux in a modern and scalable manner.
Don’t get me wrong, there still is a lot that can be solved using Redux, but I believe that ~90% of a typical React app is built around:
- Handling Shared State
- Complex State Updates and
- Data Fetching
In the following articles in this series, I’ll be diving into each of these aspects and showing my case for a Redux free architecture.
If you enjoyed this article, don’t forget to support us with a few claps 👏👏👏 or comment your thoughts and questions below!
Authors: Pubudu Dodangoda, Kenneth Ah Young