State Management with NgRx
1. Introduction
State management is a crucial aspect of building applications. When applications grow, managing state can become complex. NgRx provides a framework for managing state in Angular applications using the Redux pattern.
2. NgRx Concepts
Key Concepts
- Store: The single source of truth for the application state.
- Actions: Events that describe something that happened in the application.
- Reducers: Pure functions that take the current state and an action to return a new state.
- Selectors: Functions to query and retrieve specific pieces of state.
- Effects: Side effects that handle asynchronous operations.
3. Installation
To add NgRx to your Angular project, run the following command:
ng add @ngrx/store @ngrx/effects @ngrx/store-devtools
4. Store
The store is where your application state lives. You define the store in your application module.
import { StoreModule } from '@ngrx/store';
import { reducer } from './reducer';
@NgModule({
imports: [
StoreModule.forRoot({ state: reducer }),
],
})
export class AppModule { }
5. Actions
Actions are dispatched to change the state. Define actions using the following syntax:
import { createAction } from '@ngrx/store';
export const addItem = createAction('[Item] Add Item', (item: string) => ({ item }));
6. Reducers
Reducers are pure functions that take the current state and an action, and return the new state.
import { createReducer, on } from '@ngrx/store';
import { addItem } from './actions';
export const initialState = [];
const _itemReducer = createReducer(
initialState,
on(addItem, (state, { item }) => [...state, item])
);
export function itemReducer(state, action) {
return _itemReducer(state, action);
}
7. Effects
Effects enable side effects in your application, such as API calls. Use the following pattern:
import { Injectable } from '@angular/core';
import { Actions, ofType, createEffect } from '@ngrx/effects';
import { of } from 'rxjs';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { ApiService } from './api.service';
import { addItem } from './actions';
@Injectable()
export class ItemEffects {
loadItems$ = createEffect(() => this.actions$.pipe(
ofType('[Item] Load Items'),
mergeMap(() => this.api.getItems()
.pipe(
map(items => ({ type: '[Item] Load Items Success', items })),
catchError(() => of({ type: '[Item] Load Items Failure' }))
))
));
constructor(private actions$: Actions, private api: ApiService) {}
}
8. Selectors
Selectors are used to retrieve slices of state from the store:
import { createSelector } from '@ngrx/store';
export const selectItems = state => state.items;
export const selectItemCount = createSelector(
selectItems,
(items) => items.length
);
9. Best Practices
- Keep state minimal and flat.
- Use actions to describe state changes clearly.
- Utilize selectors for efficient state retrieval.
- Test reducers and effects thoroughly.
- Keep the store immutable.
10. FAQ
What is NgRx?
NgRx is a state management library for Angular applications based on the Redux pattern.
When should I use NgRx?
Use NgRx when your application state becomes complex and hard to manage with simple services.
Can I use NgRx without Redux?
No, NgRx is built on the Redux pattern and relies on actions, reducers, and a store.