3d cartoon hands holding a phone

Unlock full course by purchasing a membership

Lesson 4

State Management Libraries

STANDARD

State Management Libraries

The state management approach we have looked at is something that can scale well — I think it also adheres to the idea of declarative and reactive code more closely than many other state management libraries.

There is no specific reasons that you have to use a state management library — but certain libraries might provide you with certain conveniences or use a particular style that you like. Perhaps you join a team where state is handled using a state management library.

It is not a goal of this course to cover how to integrate various state management libraries, but I do want to give you an idea of some of the popular options out there that you might come across and what their general philosophy is.

SignalStore

NgRx SignalStore (not to be confused with other libraries offered by NgRx) is quite new, but is quickly becoming a sort of defacto state management solution for signals in the Angular world.

A basic implementation looks like this:

import { signalStore, withState } from '@ngrx/signals';
import { Book } from './book.model';

type BooksState = {
  books: Book[];
  isLoading: boolean;
  filter: { query: string; order: 'asc' | 'desc' };
};

const initialState: BooksState = {
  books: [],
  isLoading: false,
  filter: { query: '', order: 'asc' },
};

export const BooksStore = signalStore(
  withState(initialState)
);

This BooksStore can then be injected wherever you need to use it:

import { Component, inject } from '@angular/core';
import { BooksStore } from './books.store';

@Component({
  providers: [BooksStore],
})
export class BooksComponent {
  readonly store = inject(BooksStore);
}

Alternatively, so that you wouldn’t have to add it to the providers of a particular component, you can provide it globally like this:

export const BooksStore = signalStore(
  { providedIn: 'root' },
  withState(initialState)
);

At a basic level, we pass whatever state we want to keep track of to the store using withState. We will then be given signals for all of that state, including the nested state like query and order, e.g:

console.log(store.books())
console.log(store.isLoading())
console.log(store.filter.query())
console.log(store.filter.order())

As well as withState, SignalStore provides many other features that provide you a great deal of control over how to use and update your state.

StateAdapt

StateAdapt is a very new state management library, which does make it risky to use. However, I wanted to give it a quick special mention because I think it is the state management library that deals with the idea of coding reactive/declaratively the best.

In general, the philosophy is very much the same as the approach we are using — that is due in large part to the fact that I was heavily inspired by it when deciding on the approach we are using in this course.

The API to use it is perhaps a bit more confronting than what we have covered, but it offers a bunch of extra features.

signalSlice

Full disclosure: I co-created this utility and designed it around the way I like to think about state, so naturally this is a solution I am biased toward.

signalSlice is a lightweight state utility that is designed around similar ideas to those we have already discussed in terms of declarative code.

A basic implementation looks like this:

initialState = {
  checklists: []
};

state = signalSlice({
  initialState: this.initialState,
  sources: [this.loadChecklists$],
  actionSources: {
    add: (state, action$: Observable<AddChecklist>) =>
      action$.pipe(
        map((checklist) => ({
          checklists: [...state().checklists, checklist],
        })),
      ),
  }
});

The general idea is that we provide it with the state we want to track, and the only way that state can be updated is via any of the sources emitting values that are mapped to the state object, or via the actionSources that can be triggered on the state object, e.g:

STANDARD
Key

Thanks for checking out the preview of this lesson!

You do not have the appropriate membership to view the full lesson. If you would like full access to this module you can view membership options (or log in if you are already have an appropriate membership).