When Angular meets Redux

Preamble

We hear more and more about the growing use of the Redux concept with Angular (2+). While looking for several articles to guide me on the subject, I realized that far too few are accessible in our beautiful language of Molière. The purpose of this article is therefore to make you understand Redux, and its use with a framework which is not its favorite field. If you use Angular and are unfamiliar with this concept, you've come to the right place.

Prerequisites

This article is intended for developers who are used to using Angular without having a clear idea of ​​what Redux is. Concepts such as Observables, Typescript basics, and Components will be taken for granted. Knowledge of Functional programming is a plus.

Introduction

Redux is an architecture that aims to concentrate the state of your application in one place. Actions will be dispatched by events, which will each time resume the previous state, modify part of it, and return a new one that will be used to manage your data.
It's not very clear ? No worries, the purpose of this article is to demystify a seemingly complicated concept that turns out to be simple and logical.
Before getting to the heart of the matter, I would like to insist on the fact that Redux, very often used with React, is in no way dependent on the latter. It is high time that we understood the benefits of a very practical architecture to use with any component-oriented framework/library, and Angular is a perfect example.

What are the issues with Angular that can be solved by this new app data management?

Variables shared by different components of different levels

In an Angular component in general, it is rare to have variables in readonly for something other than service constants or Observables. The variables directly displayed in the component are also modified by this same component. But where you can have a real problem is when these variables are passed as Input to a sub-component, and they are modified by this sub-component. It's possible and it works because Angular's data-binding is made for that. However, it becomes very difficult to test it, and the code can have side effects.
Imagine a tree such as:

Component0 ├── Component1 └── Component2 └── Component3

We have a variable that we want common to components 1 and 3. So, we have to declare this variable as a property of Component0 , only to have it updated in Component2 as well as in Component3. We will have to put an output in component 1 if we want to modify the variable following an event. And do the same, not only in Component3, but also in Component2 to go up to Component0 with EventEmitter.

A lot of code, to simply modify a variable used in 2 places on the same page.

Obviously, Angular already embeds solutions that we could use in this case. The particular use of services, in which we would store the shared variable. If we wanted it to be bound in both components, i.e. it changes in real time in Component1 when it was changed in Component3, then we should use reactive programming paradigms , especially Observables with RXJS. This is doable, and many applications work this way, but it can become difficult to maintain when the pattern repeats regularly, in multiple places in the application. However, this idea should not be ruled out completely, as it will provide a basis for our solution.

Bound data is very volatile and debugging can be complicated

Data can change all the time, and unless you're constantly working in immutability, tracking changes in an object's values ​​can get tricky. It is also difficult to keep a history of actions and events carried out for a while. Redux also provides a solution at this level with its “Time-travel debugging”. Redux keeps each time a new state is created a copy of its previous version, and everything is available in the Dev Tools via the extension

It can be difficult to organize code that modifies variables in a pure way.

Here, we really go into part of the explanation of functional programming, but a solution provided by this paradigm is the concept of “pure function”. A pure function has one or more arguments, does not modify any variable outside its scope, and returns a value that will always be the same with the same arguments. It's quite simple in theory, and not always in practice.
If you want to dive deeper into explaining and understanding the functional programming paradigm, I can only recommend an article by Yoan Ribeiro on the subject.
Moreover, the use of Angular often leads us to do OOP rather than functional programming, in the sense that we modify the component data (which is a class) which is by definition external to the methods of the making up.

A bit of theory before practice

Now that we have raised a few points inherent to development with Angular and which can be simplified with Redux, we will try to understand the pattern well, by architecting a basic Redux application.

You will sometimes see Store, sometimes State. The two words cover globally the same meaning, namely the state of the application and its data structure.

This scheme is very simple. From our component, we go dispatch an action. We can translate this by the simple fact of sending a call, an event which will itself call a function.
Then this action will be applied to a reduce who will act on the state of the app. It is ultimately this same state that will be read by the component(s) in the UI.
In two sentences and a simple diagram, we could summarize the concept quite easily. But obviously, the actions, the reducer and the state each have a role. And while there may seem like a lot of code and functions to do little things, they each have great significance.
As can be seen, the flow will always have to be unidirectional, and this is an important point that will serve as the cornerstone of our architecture. User actions will go through the component that will dispatch actions. These actions will be dispatched to reducers, our pure functions which will return a new state for the application.
Earlier, we were talking about a concern for side effects when modifying variables shared by several components. Redux provides a real solution at this level by placing itself on the concept of functional programming, and not on object-oriented. A minimization of edge effects, each component, whatever it is, will only act on the state that is necessary for it. We will dispatch an action to update a property of the state. Then at each place where this property will be used in the form of a variable in a component, it will be bound and therefore modified, all in a specific action which will only have modified this property.

Example of a reducer

const reducer: Reducer<AppState> = (state: AppState, action: Action) => {
  switch (action.type) {
    case "IS_LOADING":
        return {
            ...state,
            isLoading: action.payload
        };
    default:
        return state;
  }
};

Place to practice

Here is a slightly more complicated diagram which translates a real use case within the framework of the development of an application.

NDD: a payload is the name given to a variable of any type that will be sent to the action and used by the reducer.

Let's take a case where we build an application that looks like a blog, but deals with manga (to change articles).
We will have a component that will display the data of a manga via its ID (present in the URL). Let's imagine that the title of the manga, its author and its number of volumes are data loaded when the page is displayed, but not its description. This one being very long, you have to click on a button to display it and therefore load it.
When this button is clicked, several things will happen. Our component will dispatch an action that will load the description of the manga via the function loadMangaDescription(). This function will First of all dispatch the action DESCRIPTION_IS_LOADING with an payload to true. We will want to show the user that the information is loading. We can for example, as long as this value is true, display a spinner. The component will therefore retrieve this value from the State, which was updated by the reducer called by the action DESCRIPTION_IS_LOADING.
Once in a totally synchronous way, we have updated our State to say that the description is loading, we can move on. The component not only dispatched this action, but it also called a service which will fetch the description via an HTTP call. Who says HTTP here says asynchronous, so we don't know when the response will arrive. This is usually why spinners are displayed, as in this example.
When the request returns, the service will return the result to the component which will finally be able to launch the following two actions (by connecting to the result by Promise or Subscription). The component will therefore be able to dispatch the action again DESCRIPTION_IS_LOADING this time with a payload at false.
But that's not all. The goal of our operation, from the component, is to retrieve the description of the manga. It's fine to put a spinner on while loading and remove it once the data is received, but still would need to use that data. For that, we will dispatch a second action following the first which sets the loading to false, which will be LOAD_DESCRIPTION. This action will contain in payload the data that we want to send to the reducer then find in the state. It is this data that will then be displayed on the UI, provided to the component.
The product code for this example can be found at this Github repository to get a deeper understanding of using Redux beyond theory and schematics. It is relatively simple and corresponds to an NgRedux startup, with above all the essentials for the architecture to work.

The Epics

Another concept brought by Redux is that of Epics. In the previous case, we would like for example not to be obliged to specify that once the Service call has been made and the action dispatched, we also want the loader to disappear by dispatching a new action. If we had to dispatch the action that fills the data to several places, we would then have to duplicate the code to make the loader disappear. This is where the Epics come in. An Epic is a function that will be triggered by a reducer and that will take a stream of actions to return another stream of actions. In our case, it would suffice to say that each time the action LOAD_DESCRIPTION is dispatched, we also want to dispatch the action DESCRIPTION_IS_LOADING with the payload set to false. Simple and efficient, no code duplication or additional cases to manage.
More information on the concept of Epics and their use cases:
- https://medium.com/kevin-salters-blog/epic-middleware-in-redux-e4385b6ff7c6
- https://redux-observable.js.org/docs/basics/Epics.html

Angular-Redux

Until then, one question still remains. We know how to listen to events on the component to dispatch our actions. We know how to plug a service into it if we need asynchronous data, and how to efficiently dispatch our actions to reducers so that data changes in state don't collide. Except that once we've updated our state, how to retrieve the elements is still a bit unclear.
How do you know when a variable has changed value? And how to use this new value in our component?
It is to answer these kinds of questions that libraries like Angular-Redux. This is a middleware that will allow us to act and retrieve elements from our state statically or dynamically. Here is sample code to retrieve our manga description dynamically.

import { select } from '@angular-redux/store';
import { Observable } from 'rxjs/Observable';
import { MangaState } from '../store/state';
@Component({
    selector: 'app-manga',
})
export class MangaComponent {
    @select(['manga']) readonly manga: Observable;
    constructor() {}
}

Taken from the code from the repository above and simplified, this code allows us to have an overview of retrieving our data from the store.
The first keyword in the line is the @select which is a decorator provided by angular-redux and which allows us to retrieve our manga element from the store and make it an Observable. What the type of our variable corresponds to, taking as input the MangaState interface which corresponds to the type of our object.
From there, it will be easy to display the data in HTML. However, there is a small change to make to manage the Observable.

<div *ngIf="manga | async; let manga">
    Nom du manga : {{ manga.name }}
    Auteur du manga : {{ manga.author }}
    <div>LOADING DESCRIPTION</div>
    <div>{{ manga.description }}</div>
</div>

As you can see, you have to manage the Observable asynchronously. The Observable is a stream that can have fixed values ​​at a given time, but to display these values ​​in the HTML, you have to put a pipe | async which will automatically retrieve the last value. Second remark, behind the pipe, you can create a local variable so that you don't have to do an async pipe for each value to display (which would be possible). So the {{ manga.name }} will actually display the name property of the created manga local variable, not that of the Observable.

Feedback from experience

Having worked on Angular applications from scratch with and without Redux, I would say that there is a knack for “thinking Redux” but that happens quite quickly. As when moving from an object language to a functional language, you have to get used to coding differently. But the game is definitely worth the effort, and the combination of Redux + typing + testing is a winning combo for an application extremely solid, easily debuggable and maintainable. Obviously, its implementation takes longer because a Store must be well thought out according to the needs of the application. The separation of data between different reducers can also be a point that will determine maintainability depending on the reuse of these reducers by different pages. Redux is an open architecture in which you can even add patterns corresponding to your needs, such as replacing the Epics with another system of Dispatchers who will have sole responsibility for dispatching actions, for example.

Conclusion

We see that an architecture like Redux can interface with any type of application. You just need to fully understand the usefulness of the pattern for managing data and the global state of your application.
You just have to think of extras to integrate it into Angular, such as Angular-Redux to manage the binding between component variables and the global state.
The use of Typescript takes on its full meaning, in particular via the typing of the store and the data it contains, which can be found in the reducers as well as the actions, and even the Observables in the components (see code from repository). We therefore have on all the code a security brought by the typing for avoid mistakes as much as possible.
Another important point: Redux is not magic. Of course, you have to realize that Redux is nothing more than an architecture added to your application, but it doesn't magically eliminate all the pattern problems that your application may already have. Even if the library provides a strong solution to the management of application data, the fact remains that its implementation requires time. We must think carefully about the structure of the data and its normalization, because this will have a significant impact on the architecture of the actions, the store, and the reducers. You have to take the time to type and test this data as well as the functions that modify it to keep an application healthy, functional and without surprises.
Written by William Barranco