Understanding NgRx

NgRx is a group of libraries inspired by the Redux pattern which in turn is inspired by the Flux pattern

The main purpose of this pattern is to provide a predictable state container

What is Redux ?

Redux is a predictable state conatiner for javascript applications. By using redux it is easy to manage state of data and its flow througout the application.

Why we use state management ?

Before diving more into redux concepts , lets focus on what problem it solves. Everyone must be familiar with Model-View-Controller pattern.This pattern describes the separtion between data that we call Model, the presentation i.e. View and the logic part goes in Controller.With this we ensured our application is built in a structured way. However if this application grows day by day and we are losing control of data flow, in general data flow is bidirectional. Thus the user updating in one component can affect other component and vice versa. And controlling the flow of data and making sure that all interface components update accordingly is difficult and error prone tasks. So by using redux , we can solve this problem by using a central data store in our application. The store contains the state of the application and is the single soure of truth for components. So by using we dont need to syncronize between components manaully, instead this will taken care by redux. The building block of redux are 1. Actions 2. Reducers 3. Store

We will understand more on building blocks of redux when going through NgRx - The redux inspired State management library for Angular. It uses RxJs and reactive proogramming style to provide more robust development experience.

The core idea is simple of using this pattern are

State is immutable, we cant mutate the application state.

State mutation can be done by dispatching actions.

Only pure reducer functions are used to change the application state.

In case you are not aware of pure functions , they are kind of functions that will always return the same value for same arguments. So if we adopt functional programming , we get more control over application state. By restricting the ways the data can be changed , reduces the chance of bugs.

Store

In simple words , store is database of application, it comprises of different state defined in our application. States are immutable and thus can be altered only by actions. The store combines the whole application state into a single entity or can be said as the central objects that holds the state of the application.

Reducers

Reducers are most important building block and it's important to understand the concept. A reducer is a pure function that accepts two parameters - an action and the previous state with a type and optional data associated with the event.

reducer example

export function reducer(state = initialState, action: notes.Actions):State { switch(action.type) { case 'CREATE_NOTES': return { notes: [...state.notes,action.payload] } default: return state; } }

,

Actions

Actions are used to send information from the application to the store. Sending information to the store is needed to change the application state after a user interaction, internal events or API calls.

// Action interface export interface Action { type: string, payload?: any } // Action with payload dispatch({type: 'CREATE_NOTES', payload: {id: 1, name:'Learning NgRx'}}) // Action without payload dispatch({type:'LOAD_NOTES'})

So when the action is dispatched , the reducer takes it and it applies the payload, depending on the action type and outputs the new state.

Lets us see a practical basic example to manage the counter state using NgRx

1. Create a new Angular app in nodejs cmd using

ng new counter

Let’s add the ngrx libraries that we are going to use:

npm install @ngrx/core @ngrx/store @ngrx/effects @ngrx/store-devtools @ngrx/router-store --save

store-devtools - tool for debugging

router-store - keeps the state of angular router ai the store.

2. Create a new file inside app folder of new angular app counter

src/app/counter.actions.ts

import { createAction } from '@ngrx/store'; export const increment = createAction('[Counter Component] Increment'); export const decrement = createAction('[Counter Component] Decrement'); export const reset = createAction('[Counter Component] Reset');

3. Define the reducer function to handle changes in the counter value based on the provided actions

src/app/counter.reducer.ts

import { createReducer, on } from '@ngrx/store'; import { increment, decrement, reset } from './counter.actions'; export const initialState = 0; const _counterReducer = createReducer(initialState, on(increment, state => state + 1), on(decrement, state => state - 1), on(reset, state => 0), ); export function counterReducer(state, action) { return _counterReducer(state, action); }

4. Import the StoreModule from @ngrx/store and the counter.reducer file.

src/app/app.module.ts (imports)

import { StoreModule } from '@ngrx/store'; import { counterReducer } from './counter.reducer';

5. Add the StoreModule.forRoot function in the imports array of your AppModule with an object containing the count and the counterReducer that manages the state of the counter. The StoreModule.forRoot() method registers the global providers needed to access the Store throughout your application.

src/app/app.module.ts (imports)

import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { StoreModule } from '@ngrx/store'; import { counterReducer } from './counter.reducer'; @NgModule({ declarations: [AppComponent], imports: [ BrowserModule, StoreModule.forRoot({ count: counterReducer }) ], providers: [], bootstrap: [AppComponent], }) export class AppModule {}

6. Now create a new component called show-counter inside app folder.

Inject the Store service into your component to dispatch the counter actions, and use the select operator to select data from the state.

src/app/show-counter/show-counter.component.html

Increment Current Count: {{ count$ | async }} Decrement Reset Counter

Update the ShowCounterComponent class with a selector for the count, and methods to dispatch the Increment, Decrement, and Reset actions.

src/app/show-counter/show-counter.component.ts

import { Component } from '@angular/core'; import { Store, select } from '@ngrx/store'; import { Observable } from 'rxjs'; import { increment, decrement, reset } from '../counter.actions'; @Component({ selector: 'app-show-counter', templateUrl: './show-counter.component.html', styleUrls: ['./show-counter.component.css'], }) export class ShowCounterComponent { count$: Observable<number>; constructor(private store: Store<{ count: number }>) { this.count$ = store.pipe(select('count')); } increment() { this.store.dispatch(increment()); } decrement() { this.store.dispatch(decrement()); } reset() { this.store.dispatch(reset()); } }

7. Add the ShowCounter component to your AppComponent template.

Conclusion

This article provides you a basic idea of using NgRx and its benefits , the above example just shows the basic idea but its usage is far better when using in a big application. I suggest you to play around with NgRx and keep coding . Happy coding :)