WalzoneInterview Prep
📞 Interviewing soon? Practice with a realistic AI mock phone interview — it calls you, then scores you. First 15 min FREE →

Angular · Intermediate · question 40 of 100

How do you implement state management in Angular applications using NgRx?

📕 Buy this interview preparation book: 100 Angular questions & answers — PDF + EPUB for $5

‘NgRx‘ is a library for state management in Angular applications based on the principles of the Redux pattern. It helps manage application state in a predictable and efficient way.

Here’s how you can implement state management in Angular applications using NgRx:

1. **Install NgRx packages:**

You will need to install the following packages:

- ‘@ngrx/store‘: The core package for state management.

- ‘@ngrx/effects‘: A side-effect model for processing asynchronous actions.

- ‘@ngrx/entity‘: A package to reduce boilerplate for managing entity state.

- ‘@ngrx/store-devtools‘: A developer tool that helps debugging.

To install the packages, run:

   npm install @ngrx/store @ngrx/effects @ngrx/entity @ngrx/store-devtools

2. **Define State and Actions:**

State is a single immutable data structure representing the state of your application.

   interface AppState {
     products: Product[];
     selectedProduct: Product | null;
   }

Actions represent the different ways your state can change. Every action has a type and an optional payload.

   import { createAction, props } from '@ngrx/store';

   export const loadProducts = createAction('[Product] Load Products');

   export const loadProductsSuccess = createAction(
     '[Product] Load Products Success',
     props<{ products: Product[] }>()
   );

   export const loadProductsFail = createAction(
     '[Product] Load Products Fail',
     props<{ error: any }>()
   );

   export const selectProduct = createAction(
     '[Product] Select Product',
     props<{ productId: number }>()
   );

3. **Create Reducers:**

Reducers are pure functions that take the current state and an action, and return a new state.

   import { createReducer, on } from '@ngrx/store';
   import { Product } from './product.model';
   import {
     loadProducts,
     loadProductsSuccess,
     loadProductsFail,
     selectProduct
   } from './product.actions';

   export interface ProductState {
     products: Product[];
     selectedProduct: Product | null;
   }

   export const initialState: ProductState = {
     products: [],
     selectedProduct: null
   };

   export const productReducer = createReducer(
     initialState,
     on(loadProducts, state => state),
     on(loadProductsSuccess, (state, { products }) => ({ ...state, products })),
     on(loadProductsFail, (state, { error }) => ({ ...state, error })),
     on(selectProduct, (state, { productId }) => {
       const selectedProduct = state.products.find(p => p.id === productId);
       return { ...state, selectedProduct };
     })
   );

4. **Register Reducers:**

In order to use the reducers, they need to be registered in the ‘StoreModule‘.

Import the ‘StoreModule‘ and ‘EffectsModule‘ in the ‘app.module.ts‘.

   import { StoreModule } from '@ngrx/store';
   import { EffectsModule } from '@ngrx/effects';
   import { productReducer } from './store/product.reducer';
   import { ProductEffects } from './store/product.effects';

   @NgModule({
     // ...
     imports: [
       // ...
       StoreModule.forRoot({ product: productReducer }),
       EffectsModule.forRoot([ProductEffects]),
       // If you want to use the Store Devtools
       !environment.production ? StoreDevtoolsModule.instrument() : [],
     ],
     // ...
   })
   export class AppModule {}

5. **Create and Register Effects:**

Effects are where you handle side effects like API calls, WebSocket connections, and other asynchronous processes.

   import { Injectable } from '@angular/core';
   import { Actions, createEffect, ofType } from '@ngrx/effects';
   import { EMPTY } from 'rxjs';
   import { catchError, map, mergeMap } from 'rxjs/operators';
   import { ProductService } from '../product.service';
   import {
     loadProducts,
     loadProductsSuccess,
     loadProductsFail
   } from './product.actions';

   @Injectable()
   export class ProductEffects {
     loadProducts$ = createEffect(() =>
       this.actions$.pipe(
         ofType(loadProducts),
         mergeMap(() =>
           this.productService.getProducts().pipe(
             map(products => loadProductsSuccess({ products })),
             catchError(error => of(loadProductsFail({ error })))
           )
         )
       )
     );

     constructor(
       private actions$: Actions,
       private productService: ProductService
     ) {}
   }

6. **Dispatch Actions from Components and Services:**

Now you can dispatch actions from your Angular components and services to change the state.

   import { Component } from '@angular/core';
   import { Store } from '@ngrx/store';
   import { loadProducts, selectProduct } from './store/product.actions';

   @Component({
     selector: 'app-root',
     template: `...`,
   })
   export class AppComponent {
     products$ = this.store.select('product', 'products');
     selectedProduct$ = this.store.select('product', 'selectedProduct');

     constructor(private store: Store) {
       this.store.dispatch(loadProducts());
     }

     onSelect(productId: number): void {
       this.store.dispatch(selectProduct({ productId }));
     }
   }

By following these steps, you can efficiently manage your Angular app’s state using NgRx. This allows you to handle complex application state in a predictable and maintainable way, with a traceable history of state changes.

Reading is step one. Saying it out loud is the interview. Our AI interviewer calls your phone and runs a realistic Angular interview — then scores it.
📞 Practice Angular — free 15 min
📕 Buy this interview preparation book: 100 Angular questions & answers — PDF + EPUB for $5

All 100 Angular questions · All topics