State Management with NG(RX || XS)

Uthpala Pathirana
5 min readJun 5, 2021

State management has become a topic that is frequently spoken of, especially with the SPA architecture. However, why do we use it or how should we properly set it up are some areas that we developers may not focus on, in the beginning. I agree when you say the reasons for that being the learning curve and the strict deadlines we have to meet. So here’s my attempt to present it in a simple approach.

Photo by Luca Bravo on Unsplash

If there’s one thing that our applications couldn’t exist without, that would be the data, right?
If you ever worked with a DB, you’d understand how a well-planned DB design makes our lives easier while the app grows, over a poor DB with so many tables and repeated data.

You can think of the state as an embedded DB in your front-end application

Having a state lets your application own a single source of truth. Perhaps, it’ll help to reduce the number of API calls too. When you start to manage the state throughout the application, it’s possible to config Angular’s change detection strategy to OnPush which drastically improves the app’s performance as the CD will only run if the variable in the store is updated. Redux Dev Tools is a handy Chrome extension to view how the state changed since the app init till now.

Data flow with state

Now when I say manage your state, it doesn’t necessarily mean it’s good to implement state management in every app. That decision should be certainly based on the complexity, tech stack, resource allocation, time you have, and many other factors. Having worked with both NGRX and NGXS libraries (I don’t call myself an expert though), I tell you that you’ve gone that extra mile if you’ve already integrated the state into your app. Therefore, there’s no point in having dumb components (components that don’t use the state) anymore. You need to take the maximum out of all the trouble you went through when you read the documentation, implemented all by yourself, and the trouble your app goes through with the increased bundle size :D

Among the popular state management libraries, in this article, we will talk about NGRX and NGXS which are inspired by the redux library. These libraries focus on the CQRS principle which helps to separate loads from reads and writes in a large app.

The most famous big brother — NGRX

First, create the action file

import { createAction } from '@ngrx/store';export const showSpinner = createAction('[Customer] Show_Spinner');

Next, create the reducer.

Reducer file is cleaner now with an on method instead of a switch-case. More info from here.

import { showSpinner } from './customer.actions';

export interface State {
customer: string;
isLoading: boolean;
}
export const initialState: State = {
customer: 'ABC Company',
isLoading: false
};

const _customerReducer = createReducer(initialState,
on(showSpinner, state => {...state, isLoading: !state.isLoading})
);

export function customerReducer(state, action) {
return _customerReducer(state, action);
}

How does the component comes in to play?

import { Component } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { Observable } from 'rxjs';
import { CustomerState } from '../customer.state';
import { showSpinner } from '../customer.actions';

@Component({ ... })
export class CustomerComponent {
isLoading$: Observable<boolean>;

constructor(private store: Store<CustomerState>) {
this.isLoading$ = store.pipe(select('isLoading'));
}

feedAnimals() {
this.store.dispatch(showSpinner());
}
}

If you need to send a HTTP request, there has to be another file named an effect.

loadCustomer$ = createEffect(() => this.actions$.pipe(
ofType(fromActions.loadCustomer),
mergeMap(() => this.service.getAll()
.pipe(
map(customers => ({ type: fromActions.loadCustomerSuccess, payload: customers })),
catchError(() => EMPTY)
))
)
);

The young and charming — NGXS

This library has less dependency on rxjs and is simple thus it’s easy to understand. Here there are no reducers and you modify the state directly.

First, create the action file.

import { State, Action, StateContext } from '@ngxs/store';// Actions
export class ShowSpinner {
static readonly type = '[Customer] ShowSpinner';
}
export interface CustomerStateModel {
customer: 'string';
isLoading: boolean;
}
@State<CustomerStateModel>({
customer: 'ABC Company',
defaults: {
isLoading: false
}
})
@Injectable()
export class CustomerState {
@Action(ShowSpinner)
showSpinner(ctx: StateContext<CustomerStateModel>) {
const state = ctx.getState();
ctx.setState({
...state,
isLoading: !state.isLoading
});
}
}

In the component, it’s like this.

import { Store, Select } from '@ngxs/store';
import { Observable } from 'rxjs';
import { ShowSpinner } from './customer.actions';
@Component({ ... })
export class CustomerComponent {
@Select(state => state.isLoading) isLoading$: Observable<boolean>; constructor(private store: Store) {} showLoader(name: string) {
this.store.dispatch(new ShowSpinner());
}
}

No messy code inside the constructor as you can use the ‘@select’ selector to hook up to the state variable.

Conclusion

As many authors say, NGXS reduces the boilerplate profoundly, and isn’t it evident from the above examples? But, according to my opinion, I would say the NGRX gives us the real experience of managing the state where we’ll have more control. That can be tricky because there’s a lot of chance that we do it wrong and the whole app becomes unmanageable, unpredictable, and lagging. There’s very little chance that can happen with a library like NGXS.

While using NGXS, some developers have faced an irresponsiveness when it needs to write big data to storage using a storage plugin which uses the local storage (its limit is 5MB). NGXS documents can be further improved which would eventually happen with the attraction of community. You can check out why the NGXS author wanted to write another state management framework here.

Akita is another library constructed on OOP principles, which is not just limited to Angular. Check on it here.

Nevertheless, all these state management libraries are awesome on their own. It is always wise to evaluate them yourself and arrive at decisions based on the factors affecting your project.

Thanks for tuning in. Let me know your feedback :)
Keep doing your awesome work!

--

--

Uthpala Pathirana

Someone who loves heavy rain-fall on a night and mild sun shine the following morning. Passion for travelling, reading & dancing. A typical dev.