How to Use NgRx Store in Angular: Simple Guide
To use
NgRx Store in Angular, first define your state and actions, then create reducers to handle state changes. Use Store service to dispatch actions and select state slices in your components.Syntax
NgRx Store uses these main parts:
- Actions: Describe events that change state.
- Reducers: Functions that update state based on actions.
- Selectors: Functions to get parts of the state.
- Store: Service to dispatch actions and select state.
typescript
import { createAction, createReducer, on, props } from '@ngrx/store'; import { Store } from '@ngrx/store'; // Define an action const increment = createAction('[Counter] Increment'); const add = createAction('[Counter] Add', props<{ value: number }>()); // Define initial state const initialState = 0; // Create reducer const counterReducer = createReducer( initialState, on(increment, state => state + 1), on(add, (state, { value }) => state + value) ); // Use Store in component constructor(private store: Store<{ count: number }>) {} // Dispatch action this.store.dispatch(increment()); // Select state this.store.select('count').subscribe(count => console.log(count));
Example
This example shows a simple counter using NgRx Store. It increments the count and displays it.
typescript
import { Component } from '@angular/core'; import { Store, createAction, createReducer, on, props } from '@ngrx/store'; import { Observable } from 'rxjs'; // Actions const increment = createAction('[Counter] Increment'); // Initial state const initialState = 0; // Reducer const counterReducer = createReducer( initialState, on(increment, state => state + 1) ); // Register reducer in app.module.ts (not shown here) @Component({ selector: 'app-counter', template: ` <div> <h1>Count: {{ count$ | async }}</h1> <button (click)="increment()">Increment</button> </div> ` }) export class CounterComponent { count$: Observable<number>; constructor(private store: Store<{ count: number }>) { this.count$ = store.select('count'); } increment() { this.store.dispatch(increment()); } }
Output
Count: 0 (initially)
Count: 1 (after clicking Increment button)
Common Pitfalls
1. Forgetting to register the reducer: You must add your reducer to StoreModule.forRoot() in your app module.
2. Mutating state directly: Always return new state objects in reducers; never change the existing state.
3. Not using selectors: Selecting state directly by string keys can cause issues; use createSelector for complex state.
typescript
/* Wrong: Mutating state directly */ const wrongReducer = createReducer( initialState, on(increment, state => { // If state was an object, mutation would be bad // Here, state is a number, so this is safe return state + 1; }) ); /* Right: Return new state without mutation */ const rightReducer = createReducer( initialState, on(increment, state => state + 1) );
Quick Reference
- Action: Use
createActionto define events. - Reducer: Use
createReducerandonto handle actions. - Dispatch: Use
store.dispatch(action())to trigger changes. - Select: Use
store.select(selector)to get state.
Key Takeaways
Define actions and reducers to describe and handle state changes in NgRx Store.
Use the Store service to dispatch actions and select state slices in components.
Never mutate state directly; always return new state objects in reducers.
Register reducers properly in your Angular module with StoreModule.forRoot().
Use selectors for efficient and clear state access.