Angular Snippets

Global NgRx State and NgRx Component Store

angular ngrx state

An example of a simple global NgRx Store that is used in a ComponentStore that reacts to global state changes and manipulates the rest.

Set up a global NgRx Store with Effects.

import { createAction, createFeature, createReducer, on } from "@ngrx/store";
import { createEffect } from "@ngrx/effects";
import { interval, map } from "rxjs";

interface CounterState {
  counter: number;
}

const initialCounterState: CounterState = {
  counter: 0,
};

export const incrementCounter = createAction(
  "[Angular Snippets Example] Increment Counter"
);

export const counterState = createFeature({
  name: "counter",
  reducer: createReducer(
    initialCounterState,
    on(incrementCounter, (state) => ({
      ...state,
      counter: state.counter + 1,
    }))
  ),
});

export const { selectCounter } = counterState;

export const incrementCounterEffect = createEffect(
  () => interval(1000).pipe(map(() => incrementCounter())),
  { functional: true }
);

Add your global state to the application

import { bootstrapApplication } from "@angular/platform-browser";
import { AppComponent } from "./app/app.component";
import { provideState, provideStore } from "@ngrx/store";
import {
  counterState,
  incrementCounterEffect,
} from "./app/global-state-in-component-store";
import { provideEffects } from "@ngrx/effects";

bootstrapApplication(AppComponent, {
  providers: [
    provideStore({}),
    provideState(counterState),
    provideEffects({ incrementCounterEffect }),
  ],
}).catch((err) => console.error(err));

Pull the global state into a component store

import { Component, inject, Injectable } from "@angular/core";
import { ComponentStore } from "@ngrx/component-store";
import { Store } from "@ngrx/store";
import { Observable, tap } from "rxjs";
import { AsyncPipe } from "@angular/common";

interface MyState {
  counter: number;
}

@Injectable({ providedIn: "root" })
export class ComponentStoreWithGlobalState extends ComponentStore<MyState> {
  readonly counter$ = this.select((state) => state.counter);

  private readonly store = inject(Store);

  private readonly setCounter = this.updater((state, counter: number) => ({
    ...state,
    counter,
  }));

  private readonly multiplyGlobalStateByTwo = this.effect(
    (origin$: Observable<number>) =>
      origin$.pipe(tap((counter) => this.setCounter(counter * 2)))
  );

  constructor() {
    super({ counter: 0 });

    this.multiplyGlobalStateByTwo(this.store.select(selectCounter));
  }
}

@Component({
  selector: "component-store-with-global-state",
  standalone: true,
  providers: [ComponentStoreWithGlobalState],
  imports: [AsyncPipe],
  template: ` <div>{{ counter$ | async }}</div>`,
})
export class ComponentStoreWithGlobalStateComponent {
  readonly store = inject(ComponentStoreWithGlobalState);

  readonly counter$ = this.store.counter$;
}

Use the component

import { Component } from "@angular/core";
import { ComponentStoreWithGlobalStateComponent } from "./global-state-in-component-store";

@Component({
  standalone: true,
  selector: "angular-snippet-examples-root",
  template: `
    <component-store-with-global-state></component-store-with-global-state>
  `,
  styles: [],
  imports: [ComponentStoreWithGlobalStateComponent],
})
export class AppComponent {}
Back to snippets