State Management Pattern in Angular

The State Management pattern is crucial for managing complex application states in Angular. There are several popular state management libraries available, such as NgRx (Redux-inspired), Akita, and Ngxs. Here's an example of using NgRx for state management in Angular:

Install NgRx

Open your Angular project in a terminal and run the following command to install NgRx:

Define Actions

Create action files to define the actions that can be dispatched. For example, create a file named user.actions.ts with the following code:

import { createAction, props } from '@ngrx/store';
import { User } from './user.model';
export const loadUsers = createAction('[User] Load Users');
export const loadUsersSuccess = createAction('[User] Load Users Success', props<{ users: User[] }>());
export const loadUsersFailure = createAction('[User] Load Users Failure', props<{ error: string }>());

Create Reducers

Create reducer files to define how the state changes in response to actions. For example, create a file named user.reducer.ts with the following code:

import { createReducer, on } from '@ngrx/store';
import { User } from './user.model';
import * as UserActions from './user.actions';
export interface UserState {
  users: User[];
  loading: boolean;
  error: string | null;
}
const initialState: UserState = {
  users: [],
  loading: false,
  error: null,
};
export const userReducer = createReducer(
  initialState,
  on(UserActions.loadUsers, (state) => ({ ...state, loading: true, error: null })),
  on(UserActions.loadUsersSuccess, (state, { users }) => ({ ...state, users, loading: false })),
  on(UserActions.loadUsersFailure, (state, { error }) => ({ ...state, loading: false, error }))
);

Define Selectors

Create selector files to extract specific data from the state. For example, create a file named user.selectors.ts with the following code:

import { createFeatureSelector, createSelector } from '@ngrx/store';
import { UserState } from './user.reducer';
export const selectUserState = createFeatureSelector<UserState>('user');
export const selectUsers = createSelector(selectUserState, (state) => state.users);
export const selectLoading = createSelector(selectUserState, (state) => state.loading);
export const selectError = createSelector(selectUserState, (state) => state.error);

Use NgRx in a Component

Create a component that utilizes NgRx for state management. For example, create a file named user.component.ts with the following code:

import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { User } from './user.model';
import * as UserActions from './user.actions';
import { selectUsers, selectLoading, selectError } from './user.selectors';
@Component({
  selector: 'app-user',
  template: `
    <h2>User Component</h2>
    <div *ngIf="loading">Loading...</div>
    <div *ngIf="error">{{ error }}</div>
    <ul>
      <li *ngFor="let user of users">{{ user.name }}</li>
    </ul>
  `,
})
export class UserComponent implements OnInit {
  users$: Observable<User[]>;
  loading$: Observable<boolean>;
  error$: Observable<string>;
  constructor(private store: Store) {}
  ngOnInit(): void {
    this.users$ = this.store.select(selectUsers);
    this.loading$ = this.store.select(selectLoading);
    this.error$ = this.store.select(selectError);
    this.store.dispatch(UserActions.loadUsers());
  }
}

Build and run the application: Use the following command to build and serve the Angular application:

ng serve

Your application will be accessible at http://localhost:4200.

In this example, NgRx is used for state management. Actions are dispatched to trigger state changes, reducers define how the state changes, and selectors are used to extract specific data from the state. The UserComponent subscribes to the state using NgRx's select method and displays the data in the template.

By following the State Management pattern with NgRx or other state management libraries, you can centralize and manage complex application states efficiently, leading to better code organization, maintainability, and scalability.


Similar Articles