Skip to content

Un store sur NgRx, c'est quoi ?

Un store est un concept central dans NgRx et d'autres bibliothèques de gestion d'état réactif. Il s'agit d'une sorte de conteneur qui stocke l'état de l'application de manière centralisée. Le store est accessible à partir de n'importe quel endroit de l'application, ce qui permet à tous les composants d'avoir accès aux mêmes données et d'être mis à jour en conséquence lorsque l'état de l'application change.

Un store NgRx est généralement implémenté en utilisant un Observable RxJS, qui permet de souscrire à l'état de l'application et de recevoir des notifications lorsque l'état change. Les composants de l'application peuvent s'abonner à l'Observable du store pour être notifiés des changements d'état et mettre à jour leur propre état en conséquence.

Le store NgRx permet également aux développeurs de mettre à jour l'état de l'application de manière contrôlée en utilisant des actions et des reducers. Les actions sont des objets qui représentent des intentions de modification de l'état, tandis que les reducers sont des fonctions qui prennent en entrée l'état actuel et une action, et renvoient un nouvel état mis à jour en fonction de l'action. En utilisant ces outils, les développeurs peuvent mettre à jour l'état de l'application de manière déclarative et faciliter la maintenance et l'évolution de l'application.

Ecrire un simple store

Voici un exemple simple d'un store NgRx dans une application Angular :

ts
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule } from '@ngrx/store';

import { AppComponent } from './app.component';
import { reducer } from './reducer';

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

import { AppComponent } from './app.component';
import { reducer } from './reducer';

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

Dans cet exemple, l'application Angular définit un store NgRx en utilisant la méthode StoreModule.forRoot(). Cette méthode prend en entrée un objet qui spécifie les différents états gérés par le store, ainsi que les reducers associés à chacun de ces états. Dans cet exemple, le store gère un seul état appelé counter, qui est associé au reducer reducer.

Le reducer est une fonction qui prend en entrée l'état actuel du compteur et une action, et renvoie un nouvel état mis à jour en fonction de l'action :

ts
// reducer.ts
import * as counterActions from './actions/counter';

export function reducer(state: number = 0, action: counterActions.Actions): number {
  switch (action.type) {
    case counterActions.INCREMENT:
      return state + 1;
    case counterActions.DECREMENT:
      return state - 1;
    default:
      return state;
  }
}
// reducer.ts
import * as counterActions from './actions/counter';

export function reducer(state: number = 0, action: counterActions.Actions): number {
  switch (action.type) {
    case counterActions.INCREMENT:
      return state + 1;
    case counterActions.DECREMENT:
      return state - 1;
    default:
      return state;
  }
}

Dans cet exemple, le reducer gère deux actions : INCREMENT et DECREMENT, qui incrémentent ou décrémentent le compteur en fonction de l'action. Si l'action n'est pas gérée par le reducer, le reducer renvoie l'état actuel inchangé.

Le composant AppComponent peut ensuite accéder au store NgRx en injectant le Store dans son constructeur :

ts
// app.component.ts
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';

import * as fromRoot from './reducers';
import * as counterActions from './actions/counter';

@Component({
  selector: 'app-root',
  template: `
    <h1>Compteur : {{ counter$ | async }}</h1>
    <button (click)="increment()">Incrémenter</button>
    <button (click)="decrement()">Décrémenter</button>
  `
})
export class AppComponent {
  counter$: Observable<number>;

  constructor(private store: Store<fromRoot.State>) {

  }
}
// app.component.ts
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';

import * as fromRoot from './reducers';
import * as counterActions from './actions/counter';

@Component({
  selector: 'app-root',
  template: `
    <h1>Compteur : {{ counter$ | async }}</h1>
    <button (click)="increment()">Incrémenter</button>
    <button (click)="decrement()">Décrémenter</button>
  `
})
export class AppComponent {
  counter$: Observable<number>;

  constructor(private store: Store<fromRoot.State>) {

  }
}