One of the most challenging things in software development is state management. Currently there are several state management libraries for Angular apps: NGRX, NGXS, Akita...
But what if you don't want to learn, setup, and deal with all the boilerplate for a simple project, what if you want to manage state by only using tools you already know well as an Angular developer.
In this write up, I'll show you a simple way of managing state by only using RxJS and Dependency Injection with NgSimpleState.
Step 1: install ng-simple-state
npm i ng-simple-state
Step 2: Import NgSimpleStateModule
into your AppModule
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { CommonModule } from '@angular/common';
import { NgSimpleStateModule } from 'ng-simple-state';
import { environment } from '../environments/environment';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
CommonModule,
NgSimpleStateModule.forRoot({
enableDevTool: !environment.production, // Enable Redux DevTools only in developing
})
],
bootstrap: [AppComponent],
})
export class AppModule {}
Step 3: Create your store
This is an example for a counter store in a src/app/counter-store.ts
file.
Obviously, you can create every store you want with every complexity you need.
1) Define yuor state interface, eg.:
export interface CounterState {
count: number;
}
2) Define your store service by extending NgSimpleStateBaseStore
, eg.:
import { Injectable } from '@angular/core';
import { NgSimpleStateBaseStore } from 'ng-simple-state';
export interface CounterState {
count: number;
}
@Injectable()
export class CounterStore extends NgSimpleStateBaseStore<CounterState> {
}
3) Implement initialState()
method and provide the initial state of the store, eg.:
import { Injectable } from '@angular/core';
import { NgSimpleStateBaseStore } from 'ng-simple-state';
export interface CounterState {
count: number;
}
@Injectable()
export class CounterStore extends NgSimpleStateBaseStore<CounterState> {
initialState(): CounterState {
return {
count: 0
};
}
}
4) Implement one or more selectors of the partial state you want, in this example selectCount()
eg.:
import { Injectable } from '@angular/core';
import { NgSimpleStateBaseStore } from 'ng-simple-state';
import { Observable } from 'rxjs';
export interface CounterState {
count: number;
}
@Injectable()
export class CounterStore extends NgSimpleStateBaseStore<CounterState> {
initialState(): CounterState {
return {
count: 0
};
}
selectCount(): Observable<number> {
return this.selectState(state => state.count);
}
}
5) Implement one or more actions for change the store state, in this example increment()
and decrement()
eg.:
import { Injectable } from '@angular/core';
import { NgSimpleStateBaseStore } from 'ng-simple-state';
import { Observable } from 'rxjs';
export interface CounterState {
count: number;
}
@Injectable()
export class CounterStore extends NgSimpleStateBaseStore<CounterState> {
initialState(): CounterState {
return {
count: 0
};
}
selectCount(): Observable<number> {
return this.selectState(state => state.count);
}
increment(increment: number = 1): void {
this.setState(state => ({ count: state.count + increment }));
}
decrement(decrement: number = 1): void {
this.setState(state => ({ count: state.count - decrement }));
}
}
Step 3: Inject your store into the providers of the module you want (or the providers of component), eg.:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { CommonModule } from '@angular/common';
import { NgSimpleStateModule } from 'ng-simple-state';
import { environment } from '../environments/environment';
import { CounterStore } from './counter-store';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
CommonModule,
NgSimpleStateModule.forRoot({
enableDevTool: !environment.production, // Enable Redux DevTools only in developing
enableLocalStorage: false // Local storage state persistence is globally disabled
})
],
bootstrap: [AppComponent],
providers: [CounterStore] // The CounterStore state is shared at AppModule level
})
export class AppModule {}
Step 4: Use your store into the components, eg.:
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { CounterStore } from './counter-store';
@Component({
selector: 'app-root',
template: `
<h1>Counter: {{ counter$ | async }}</h1>
<button (click)="counterStore.decrement()">Decrement</button>
<button (click)="counterStore.increment()">Increment</button>
`,
})
export class AppComponent {
public counter$: Observable<number>;
constructor(public counterStore: CounterStore) {
this.counter$ = this.counterStore.selectCount();
}
}
That's all!
see the live demo.
Top comments (0)