Managing state in large-scale Angular applications can be challenging. NgRx provides a reactive state management solution inspired by Redux that works with Angular's unidirectional data flow. This guide covers the fundamentals of NgRx and how to integrate it into your Angular applications.
NgRx is a framework for building reactive applications in Angular. It provides libraries for managing global and local state, isolation of side effects, entity collection management, and integration with the Angular Router.
- State: Described by a single immutable data structure managed by NgRx store, which is a controlled state container designed to help write performant, consistent applications.
- Actions: Express unique events that are dispatched from components and services.
- Reducers: Pure functions that take the previous state and the latest action to compute a new state.
- Effects: Model side effects such as fetching data, long-running tasks that produce other actions.
Integrating NgRx into an Angular application involves setting up reducers, actions, and effects to facilitate the features of Redux-like state management.
-
Install NgRx
Start by installing NgRx in your project with npm or yarn:
npm install @ngrx/store @ngrx/effects @ngrx/store-devtools @ngrx/router-store --save
-
Define Actions
Actions define unique events that change the state. Define actions using the
createAction
function:import { createAction } from '@ngrx/store'; export const loadItems = createAction('[Item List] Load Items'); export const addItemSuccess = createAction('[Item List] Add Item Success', props<{ item: any }>());
-
Create Reducers
Reducers handle state changes. Create a reducer using the
createReducer
function:import { createReducer, on } from '@ngrx/store'; import { loadItems, addItemSuccess } from './item.actions'; export const initialState = { items: [] }; const _itemReducer = createReducer( initialState, on(loadItems, state => ({ ...state })), on(addItemSuccess, (state, { item }) => ({ ...state, items: [...state.items, item] })) ); export function itemReducer(state, action) { return _itemReducer(state, action); }
-
Configure Effects
Effects handle asynchronous operations and side effects. Define effects using the
createEffect
function:import { Injectable } from '@angular/core'; import { Actions, ofType, createEffect } from '@ngrx/effects'; import { of } from 'rxjs'; import { map, mergeMap, catchError } from 'rxjs/operators'; import { ItemService } from './item.service'; import { loadItems, addItemSuccess } from './item.actions'; @Injectable() export class ItemEffects { loadItems$ = createEffect(() => this.actions$.pipe( ofType(loadItems), mergeMap(() => this.itemService.getAll() .pipe( map(items => ({ type: '[Items API] Items Loaded Success', payload: items })), catchError(() => of({ type: '[Items API] Items Loaded Error' })) ) ) )); constructor( private actions$: Actions, private itemService: ItemService ) {} }
-
Register State and Effects in the Module
Finally, import your state and effects into the Angular module:
import { StoreModule } from '@ngrx/store'; import { EffectsModule } from '@ngrx/effects'; import { itemReducer } from './reducers/item.reducer'; import { ItemEffects } from './effects/item.effects'; @NgModule({ declarations: [AppComponent], imports: [ BrowserModule, StoreModule.forRoot({ items: itemReducer }), EffectsModule.forRoot([ItemEffects]) ], providers: [], bootstrap: [AppComponent] }) export class AppModule {}
Utilizing NgRx in your Angular applications provides a robust, scalable approach to managing state with reactive programming principles, making it ideal for complex applications with high performance demands.