Introduction to state management with Ngrx and Angular

Introduction 🚩 :

Hello dear readers and followers, In this Blog (2021 blogging challenge ) I will try to introduce to you the concept of state management pattern with Angular application by explaining the core concepts with full-example code for each one,

Happy reading :)

What is State Management 📝

Before defining the state management, let’s define what is a State

“A state is a representation of a system in a given time.”

When it comes to front-end applications, the state can be :

  • Navigation State: where the currentUser and the current route with current params?
  • UI State: a modal is open or not, the button submit disabled or enabled, etc...
  • Application State: the kind of state that can be sued by different parts of the application like the current user is logged or not
  • Network State: if there is a current process of fetching data from APIS
  • Communication state: values that describe requests to other servers (such as a “loading” value)

Problem :

Nowadays, web applications are getting growth in complexity, by consequence managing tasks like (sharing data between components, network, user actions, UI state, etc ) becomes a hard task

Defining a way that takes care of defining the global state across all the applications with the operations to manipulate this state ( add, delete, change, etc ..) and facilitate communication and sharing of data across components

Benefits of State Management 🔥 💥 :

Applying the state management pattern to your front-end application can offer a lot of advantages and benefits :

  • Centralized State: State in a store is kept in one directory. It makes it easier to foretell updates or changes to the store, and track down problems ( single source of truth ) ✔️
  • Performance (Data flows in one direction) ✔️
  • Applications become easier to test (more organized, State representation is easy to read, data accessible whenever we need it)✔️
  • debugging easier ✔️
  • Automatically sync with local storage. This makes you retain the data whenever you refresh the page ✔️
  • Avoiding a lot of unnecessary APIS calls ✔️

Use cases for state management : 💶 💶

There some common-use cases the requires to apply a state management architecture in our application for example where :

  • There are variables that need to be accessed/shared in multiple components (get current user )
  • Update a page after creating/updating data without refresh
  • Ui global theme changes across the application (Dark mode example )

How Data Flows Without State Management with Angular ❓

For Angular projects, one of the common solutions used is sharing data by services, The much the number of features increases, the number of services increases with can result in some difficulties in data management and project maintenance!

As it’s mentioned in the following picture, without a state management solution, the data is everywhere, We don’t have a single source of truth for our data and the project looks very difficult to maintain. 🙏🏻 🙏🏻 🙏🏻

Available tooling for State Management with Angular ✒️ ✂️:

There are some popular tools to make state management for our Angular application

Native Rxjs ☝️ :

We can make the data sharing between different parts of our application by using simple Rxjs operators like BehaviorSubject to hold the state and pipeable operators to modify the state.

AKITA ↖️:

Akita is a state management pattern, built on top of RxJS, which takes the idea of multiple data stores from Flux and the immutable updates from Redux, along with the concept of streaming data, to create the Observable Data Store model.

NGXS ⚓:

NGXS is modeled after the CQRS pattern popularly implemented in libraries like Redux and NGRX but reduces boilerplate by using modern TypeScript features such as classes and decorators.

Ngrx ✈️ ✈️ :

Ngrx is a group of Angular libraries for reactive extensions that implements the Redux pattern and it’s supercharged with RXJS

it provides a solution for state management with Angular application

was built by Rxjs superpower (RxJS. BehaviorSubject, Subject and Observable)

Sincerely, Ngrx was the most famous library used for state management, I didn’t tie the other tools to make some deep technical comparison

So as a beginner and the title of the blog said “Introduction to Ngrx

source : https://ngrx.io/guide/store

The Store is the database for our front-end application

It combines all the application different states in one entity what we call a single source of truth and is immutable

The state presents the data structure of the make-ups the store, the fields in the database!

It can be server-side API response, user input, router state

The state lives in the context of the store

Actions present the events that will be triggered by the user to update the store,

“they represent payloads of information that are dispatched to the store from the application”

“ In code, an action is represented by a plain old JavaScript object with two main attributes, namely type and payload. payload"

Example definition of An action :

And this how should be called inside your component to

if we can alternative with a traditional database, the Store is the whole database, the reducers ate the tables

it’s a pure function responsible for changing the state that accepts two arguments: the action and the previous state, make the modification and return the new state object

based on the action type the reducer will perform modification to the current state and produce a new one

w nfasr on operator , w code kfeeh

  • Effect: “A mechanism that listens for dispatched actions in an observable stream, processes the server response, and returns new actions either immediately or asynchronously to the reducer to change the state. Please note that we are not using ‘effect’ in this example app.”

Effects to perform side effects when an action is dispatched to the store

The @ngrx/effects library provides a way to isolate side effects into its own model

Selector: Selector is a function used for obtaining a part of the state from the store.

They are the queries for our store

Selectors advantages :

“A selector is a pure function that maintains a memory of the previous executions. As long as the input hasn’t changed, the output will not be recalculated”

  • the learning curve when you first start working with NgRx.
  • The application will be a bit verbose( introduce several concepts reducers, selectors, effects, new artficats , bundle size!
  • NgRx exponentially increases the code complexity with a hell of a lot of unnecessary boilerplate code

Application Example (counter-app)💻 :

In this part, I will try to explain to you a small application build with angular and Ngrx to master and manipulate some theoretical concepts that we discussed in the other parts

In the first step, we will generate a project with angular-CLI

ng new ngrx-poc --style=scss --routing=false

Just type code. in your terminal with the current project directory

code .

Follow these commands to run the project

cd ngrx-pocnpm start

We need to install Ngrx dependencies and devtools

npm install @ngrx/store --savenpm install @ngrx/effects --savenpm install @ngrx/store-devtools --save

The first thing that we will define is the store structure that will be used across our application

By creating file counter.state.ts under /app/store

export interface CounterState {counter: number;}export const initialState: CounterState = {counter: 4};

Next step we will define the different actions that can be applied to our store,

Create a file counter.actions.ts under /store

import { createAction, props } from '@ngrx/store';export const increment = createAction('increment');export const decrement = createAction('decrement');export const reset = createAction('reset');export const incrementAction = createAction('[Counter] Increment',props<{ count: number }>());

After defining our store and actions, let’s prepare the reducer the function the element responsible for updating the store

import { Action, createReducer, on, State } from '@ngrx/store';import { CounterState, initialState } from './counter.state';import * as CounterActions from './counter.actions';const counterReducer = createReducer(initialState,on(CounterActions.increment, (state) => {return {...state,counter: state.counter + 2,};}),on(CounterActions.decrement, (state) => {return {...state,counter: state.counter - 2,};}),on(CounterActions.reset, (state) => {return {...state,counter: 0,};}));export function reducer(state: CounterState | undefined, action: Action) {return counterReducer(state, action);}

As we explained in the previous part, the selectors are the part responsible for selecting the necessary data from the state

Create a counter.selector.ts file under the /store folder

import { createFeatureSelector, createSelector } from '@ngrx/store';import { CounterState } from './counter.state';export const COUNTER_STATE_NAME = 'counter';export const selectCounter =createFeatureSelector<CounterState>(COUNTER_STATE_NAME);export const getCount = createSelector(selectCounter,(state: CounterState) => state.counter);

We need now the import the StoreModule to the app module and linking the reducer

@NgModule({declarations: [AppComponent],imports: [BrowserModule,StoreModule.forFeature(COUNTER_STATE_NAME,reducer)],providers: [],bootstrap: [AppComponent]})export class AppModule { }

Now, we need to define the UI part of our application, by defining the container component that will be counter-component and the presentation component that will be ( counter-buttons, counter-output )

ng g c counter
ng g c counter-buttons 

add this HTML code to counter-buttons.component.html

<div><button class="btn btn-primary" (click)="onIncrement()">Increment</button>&nbsp;<button class="btn btn-warning" (click)="onDecrement()">Decrement</button>&nbsp;<button class="btn btn-info" (click)="onReset()">Reset</button></div>

and this ts code to counter-buttons.component.ts :

As it’s mentioned in the code we need to inject the ‘store: Store<CounterState> inside our component to connect it to our store

import { Component, OnInit } from '@angular/core';import { Store } from '@ngrx/store';import { decrement, increment, reset } from '../store/counter.actions';import { CounterState } from '../store/counter.state';@Component({selector: 'app-counter-buttons',templateUrl: './counter-buttons.component.html',styleUrls: ['./counter-buttons.component.scss']})export class CounterButtonsComponent implements OnInit {constructor(private store: Store<CounterState>) { }ngOnInit(): void {}onIncrement(){this.store.dispatch(increment())}onDecrement(){this.store.dispatch(decrement())}onReset(){this.store.dispatch(reset())}}

“this.store.dispatch(increment())” is an example of how to dispatch an action that will be relieved by the reducer which updates the state (the flux pattern data flow)

Now we need to define a new component ‘counter-output’ a presentational component to display the current state counter value!

counter-output.component.ts:

this.store.select(getCount)” to select the counter value from the store

import { Component, OnInit } from '@angular/core';import { Store } from '@ngrx/store';import { Observable } from 'rxjs';import { getCount } from '../store/counter.selector';import { CounterState } from '../store/counter.state';@Component({selector: 'app-counter-output',templateUrl: './counter-output.component.html',styleUrls: ['./counter-output.component.scss']})export class CounterOutputComponent implements OnInit {counter$ : Observable<number>constructor(private store: Store<CounterState>) {}ngOnInit(): void {this.counter$ = this.store.select(getCount)}}

counter-output.component.html:

display the current counter value with async | pipe :

<div><h3>Counter is: {{ counter$ | async }}</h3></div>

And finally, this is how we should integrate the child components code inside

counter.component.html

<div class="row"><div class="col-md-12"><div class="my-6"><app-counter-output></app-counter-output></div><div class="my-6"><app-counter-buttons></app-counter-buttons></div></div></div>

app.component.html

<div><app-counter></app-counter></div>
npm start

You can find the source code: https://github.com/Rebaiahmed/learning-angular-advanced/tree/main/ngrx/ngrx-poc

Appendix 📘 📙 :

Zero Ngrx BoilerplateYou may never write an action, reducer, selector, effect, or HTTP data service again.

is a great way to manage immutable collections.

Bindings to connect the Angular Router with Store. During each router navigation cycle, multiple actions are dispatched that allow you to listen for changes in the router’s state. You can then select data from the state of the router to provide additional information to your application

provides scaffolding. NgRx commands get integrated into the Angular CLI, and most of the NgRx elements can be created using angular CLI. So, let’s add NgRx Schematics. (You can use a new terminal window or exit out from the running angular app by pressing the Ctrl+C key )

  • ng add @ngrx/schematics@latest

Store Devtools provides developer tools and instrumentation for Store.

Example Ngrx devtools screenshot

Advanced logging for @ngrx/store applications, ported from redux-logger.

Simple syncing between ngrx store and local or session storage.

is a set of extensible dev tools for monorepos that extends the Angular CLI. Nx comes with a set of utilities that help writing effects dealing with the router and server communication simpler

Conclusion ❤️:

For Conclusion, I tried to explain the concept of state management with Angular application pros, cons, and core concepts,

And I added a small example to explore Ngrx tools and terminology with angular

If you have my questions feel free to add your comment or to open an issue in the Github repo

Learn by coding

<script>alert('try your best')</script>