State management
An EcmaScript
class backs every component and service in Angular. When instantiated, a class becomes an object in memory. As you work with an object, if you store values in object properties, you’re introducing state to your Angular application. If unmanaged, the state becomes a significant liability to the success and maintainability of your application.
I’m a fan of stateless design both in the backend and frontend. From my perspective, state is evil, and you should pay careful attention to not introduce state into your code. Earlier, we discussed how services in Angular are singletons by default. This is a terrible opportunity to introduce state to your application. You must avoid storing information in your services. In Chapter 4, Creating a Router-First Line-of-Business App, I introduce you to readonly
BehaviorSubject
, which acts as a data anchor for your application. In this case, we store these anchors in services to share them across components to synchronize data. The data anchor is a reference to the data instead of a copy. The service doesn’t store any metadata or do any bookkeeping.
In Angular components, the class is a ViewModel acting as the glue code between your code and the template. Components are relatively short-lived compared to services, and it is okay to use object properties in this context.
However, beyond design, there are specific use cases for introducing robust mechanisms to maintain complicated data models in the state of your application. Progressive web applications (PWA) and mobile applications are cases where connectivity is not guaranteed. In these cases, being able to save and resume the entire state of your application is a must to provide a great UX for your end user.
The NgRx library for Angular leverages the Flux pattern to enable sophisticated state management for your applications. In Chapter 2, Forms, Observables, Signals, and Subjects, and Chapter 9, Recipes – Master/Detail, Data Tables, and NgRx, I provide alternative implementations for various features using NgRx to demonstrate the differences in implementation between more lightweight methods.
The Flux pattern
Flux is the application architecture created by Facebook to assist in building client-side web applications. The Flux pattern defines a series of components that manage a store that stores the state of your application, via dispatchers that trigger/handle actions and view functions that read values from the store. Using the Flux pattern, you keep the state of your application in a store where access to the store is only possible through well-defined and decoupled functions, resulting in architecture that scales well because, in isolation, decoupled functions are easy to reason with and write automated unit tests for.
Consider the diagram that follows to understand the flow of information between these components:
Figure 1.17: NgRx data flow
NgRx implements the Flux pattern in Angular using RxJS.
NgRx
The NgRx library brings Redux-like (a popular React.js library) reactive state management to Angular based on RxJS. State management with NgRx allows developers to write atomic, self-contained, and composable pieces of code, creating actions, reducers, and selectors. This kind of reactive programming allows side effects in state changes to be isolated and feels right at home with the general coding patterns of React.js. NgRx creates an abstraction layer over already complex and sophisticated tooling like RxJS.
There are excellent reasons to use NgRx, such as if you deal with 3+ input streams in your application. In such a scenario, the overhead of dealing with so many events makes it worthwhile to introduce a new coding paradigm to your project. However, most applications only have two input streams: REST APIs and user input. NgRx may make sense for offline-first Progressive Web Apps (PWAs), where you may have to persist and restore complicated state information (or niche enterprise apps with similar needs).
Here’s an architectural overview of NgRx:
Figure 1.18: NgRx architectural overview
Consider the very top of the diagram as an observable action stream, where actions can be dispatched and acted upon as denoted by the circles. Effects and components can dispatch an action. Reducers and effects can act upon these actions to either store values in the store or trigger an interaction with the server. Selectors are leveraged by components to read values from the store.
Given my positive attitude toward minimal tooling and a lack of definite necessity for NgRx beyond the niche audiences previously mentioned, I do not recommend NgRx as a default choice. RxJS/BehaviorSubject
are powerful and capable enough to unlock sophisticated and scalable patterns to help you build great Angular applications, as is demonstrated in the chapters that lead up to Chapter 9, Recipes – Master/Detail, Data Tables, and NgRx.
You can read more about NgRx at https://ngrx.io.
NgRx component store
The NgRx component store, with the package name @ngrx/component-store
, is a library that aims to simplify state management by targeting local/component states. It is an alternative to a reactive push-based subject-in-a-service approach. For scenarios where the state of a component is only changed by the component itself or a small collection of components, you can improve the testability, complexity, and performance of your code by using this library.
In contrast to global-state solutions like NgRx, the NgRx component store, with its limited scope, can automatically clear itself when its associated view is detached from the component tree. Unlike a singleton service, you can have multiple instances of a component store, enabling distinct states for different components. Additionally, the conceptual model for the component store is straightforward. One only needs to grasp the select, updater, and effect concepts, all operating within a confined scope. Hence, for those crafting a standalone Angular app or seeking component-specific storage, the NgRx component store provides a sustainable and easily testable approach.
You can find out more about the NgRx component store at https://ngrx.io/guide/component-store.