Redux and the unidirectional data flow

During the last few years, some client-side state management solutions have gained traction and have become hugely popular in the JavaScript community. These include Flux and its grandchildRedux, both of which were created by Facebook:

Both were introduced to try and solve the state management issue that impacted Facebook more and more as its user interface grew in size and complexity.

Redux is related to React, but can actually be used with other frameworks, including Vue.js and Angular. There are also other implementations of Redux, such as NGRX (https://github.com/ngrx) for Angular, which makes use of RxJS.

We won't be using Redux in this book, but nevertheless, it is very interesting to understand what it is and how it works.

Redux aims to help you write applications that do the following:

Redux (https://redux.js.org) is, in fact, both an architectural pattern and a library.

The Redux pattern prescribes how data/state changes should flow in the application so as to maximize predictability and consistency. Simply put, the Redux pattern recommends a unidirectional data flow. We'll discover what this actually means soon.

On the other hand, the main goal of the library is to provide a predictable state container or store for JavaScript applications.

Redux also focuses on the developer experience and provides great support for debugging. Notably, it supports time travel debugging through web browser extensions. The idea behind time travel debugging is that, through browser extensions, you can explore the content of the store, replay past events, or even go back in time.

With Redux, the whole state of the application is stored in an object tree within a store, which acts as the single source of truth for the whole application. Any component within the application can subscribe to the store and be notified whenever the subset of the stored data it cares about has changed.

Here's an updated version of the schema that we discussed earlier:

With this approach, when the component in dark gray pushes information into the store, all the light gray components that have subscribed are notified of the change. This is much more efficient than the naive approach of using custom events and props. It is also cleaner than blind broadcasting. This alone makes Redux a much more interesting solution, but it doesn't stop there.

If we zoom in a bit, we can see how the state data flows within the application:

The component in dark gray triggers an action, which ends up updating the state of the application, which, in turn, causes an update of the view. This is the unidirectional data flow.

If we zoom in even further, then we can see what a pattern looks like in action:

All of the components in the application dispatch Actions, which are handled by Reducers, which in turn update the store. Also, components can observe the store and the store will notify them whenever the slice of the state they care about has changed.

This way, the application's behavior can easily be observed and the data flow becomes clear and predictable.

With Redux, the store is also immutable and read-only. The only way to change it is to dispatch an action and let the reducers derive a new state. Deriving a new state means constructing a new state object tree, not mutating the existing one! This immutability constraint is the one that makes it possible to travel back in time. By keeping track of actions and store state changes, it becomes very easy to replay events and put the application back into a previous state (simply by replacing the current state with a previous one).

When an application follows the Redux pattern, it has to implement the following:

So, what data should be put inside the store?

In any case, don't go overboard: only the state that is relevant to one component should remain local to it. The store is only useful for shared states.

The store is actually a tree data structure; you can organize its content as you see fit (flat, nested – whatever you want). Usually, the recommendation is to normalize the state tree in order to limit the difficulty of updating it. This means we avoid having the same information in multiple places. You can find out more about this here: https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape.

As we've already mentioned, Redux can be used with Vue.js, as explained at the following link: https://snipcart.com/blog/redux-vue.