State Management in Angular with NgRx
A practical guide to managing application state in Angular using NgRx for predictable and scalable applications.
Introduction to State Management
In a large application, managing the state can become complex. Components might need to share information, and keeping data consistent across the app is crucial. This is where state management libraries like NgRx come in.
What is NgRx?
NgRx is a framework for building reactive applications in Angular. It provides a set of libraries for managing state in a predictable way, inspired by Redux. The core concepts are:
- Store: A single, immutable data structure that holds the entire state of your application.
- Actions: Descriptions of events that have happened, like a user clicking a button.
- Reducers: Pure functions that take the current state and an action to produce a new state.
- Selectors: Functions that extract pieces of information from the store.
- Effects: Used for handling side effects, such as fetching data from an API.
Setting up NgRx
First, you need to add NgRx to your Angular project.
ng add @ngrx/store@latest
ng add @ngrx/effects@latest
ng add @ngrx/store-devtools@latest
Example: A Simple Counter
Let's create a simple counter feature.
1. Define Actions
// counter.actions.ts
import { createAction } from '@ngrx/store';
export const increment = createAction('[Counter Component] Increment');
export const decrement = createAction('[Counter Component] Decrement');
export const reset = createAction('[Counter Component] Reset');
2. Create a Reducer
// counter.reducer.ts
import { createReducer, on } from '@ngrx/store';
import { increment, decrement, reset } from './counter.actions';
export const initialState = 0;
export const counterReducer = createReducer(
initialState,
on(increment, (state) => state + 1),
on(decrement, (state) => state - 1),
on(reset, (state) => 0)
);
3. Use it in your Component
// my-counter.component.ts
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { increment, decrement, reset } from './counter.actions';
@Component({
selector: 'app-my-counter',
template: `
<button (click)="increment()">Increment</button>
<button (click)="decrement()">Decrement</button>
<button (click)="reset()">Reset</button>
<div>Current Count: {{ count$ | async }}</div>
`,
})
export class MyCounterComponent {
count$: Observable<number>;
constructor(private store: Store<{ count: number }>) {
this.count$ = store.select('count');
}
increment() {
this.store.dispatch(increment());
}
decrement() {
this.store.dispatch(decrement());
}
reset() {
this.store.dispatch(reset());
}
}
This simple example shows the power and predictability of NgRx for managing your application's state.