Bird
Raised Fist0
Angularframework~5 mins

NgRx store concept in Angular

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Introduction

The NgRx store helps keep your app's data in one safe place. It makes it easy to share and update data across parts of your app without confusion.

When you want to share data between different parts of your Angular app.
When your app has complex data that changes often and needs to stay in sync.
When you want to keep track of app state changes clearly and predictably.
When you want to make debugging easier by seeing how data changes over time.
Syntax
Angular
import { createAction, createReducer, on } from '@ngrx/store';
import { Store } from '@ngrx/store';

// Define actions
const increment = createAction('[Counter] Increment');
const decrement = createAction('[Counter] Decrement');

// Define initial state
const initialState = 0;

// Create reducer
const counterReducer = createReducer(
  initialState,
  on(increment, state => state + 1),
  on(decrement, state => state - 1)
);

// Use Store in component
constructor(private store: Store<{ count: number }>) {}

// Dispatch action
this.store.dispatch(increment());

// Select state
this.store.select('count').subscribe(value => console.log(value));

Actions describe what happened, reducers describe how state changes.

The store holds the app state and lets components read or update it.

Examples
This creates an action named 'loadItems' to signal loading items.
Angular
import { createAction } from '@ngrx/store';

const loadItems = createAction('[Items] Load Items');
This reducer updates the state to show loading when 'loadItems' action runs.
Angular
import { createReducer, on } from '@ngrx/store';

const initialState = { items: [] };

const itemsReducer = createReducer(
  initialState,
  on(loadItems, state => ({ ...state, loading: true }))
);
This shows how to read the 'items' state from the store in a component.
Angular
constructor(private store: Store<{ items: any[] }>) {}

this.store.select('items').subscribe(items => console.log(items));
Sample Program

This Angular component shows a simple counter using NgRx store. Buttons dispatch actions to update the count. The count value updates automatically from the store.

Angular
import { Component } from '@angular/core';
import { Store, createAction, createReducer, on } from '@ngrx/store';

const increment = createAction('[Counter] Increment');
const decrement = createAction('[Counter] Decrement');

const initialState = 0;

const counterReducer = createReducer(
  initialState,
  on(increment, state => state + 1),
  on(decrement, state => state - 1)
);

@Component({
  selector: 'app-counter',
  template: `
    <h1>Counter: {{ count }}</h1>
    <button (click)="incrementCount()">Increment</button>
    <button (click)="decrementCount()">Decrement</button>
  `
})
export class CounterComponent {
  count = 0;

  constructor(private store: Store<{ count: number }>) {
    this.store.select('count').subscribe(value => this.count = value);
  }

  incrementCount() {
    this.store.dispatch(increment());
  }

  decrementCount() {
    this.store.dispatch(decrement());
  }
}

// Note: In a real app, you would register 'counterReducer' in StoreModule.forRoot({ count: counterReducer })
OutputSuccess
Important Notes

Always register your reducers in the Angular module using StoreModule.forRoot().

Use actions to describe changes, never change state directly.

Subscribe to store selectors to get updated state in your components.

Summary

NgRx store keeps app data in one place for easy sharing and updating.

Use actions and reducers to manage state changes clearly.

Components read state from the store and dispatch actions to update it.

Practice

(1/5)
1. What is the primary purpose of the NgRx Store in an Angular application?
easy
A. To style components with CSS dynamically
B. To handle HTTP requests and responses automatically
C. To keep all application data in one central place for easy access and updates
D. To manage routing between different pages

Solution

  1. Step 1: Understand NgRx Store role

    The NgRx Store is designed to hold the application state in one place.
  2. Step 2: Compare with other options

    Options B, C, and D describe other Angular features, not the store's purpose.
  3. Final Answer:

    To keep all application data in one central place for easy access and updates -> Option C
  4. Quick Check:

    NgRx Store = Central app data storage [OK]
Hint: NgRx Store = single source of truth for app data [OK]
Common Mistakes:
  • Confusing store with routing or HTTP services
  • Thinking store manages styles or UI directly
2. Which of the following is the correct way to dispatch an action named loadItems using NgRx Store in a component?
easy
A. this.store.call(loadItems);
B. this.store.emit(loadItems);
C. store.dispatch = loadItems();
D. this.store.dispatch(loadItems());

Solution

  1. Step 1: Recall NgRx dispatch syntax

    Actions are dispatched by calling this.store.dispatch(action()).
  2. Step 2: Check other options for syntax errors

    Options A, B, and C use incorrect methods or assignment instead of dispatch call.
  3. Final Answer:

    this.store.dispatch(loadItems()); -> Option D
  4. Quick Check:

    Dispatch action = this.store.dispatch(action()) [OK]
Hint: Dispatch actions with this.store.dispatch(action()) [OK]
Common Mistakes:
  • Using emit or call instead of dispatch
  • Assigning dispatch instead of calling it
3. Given this reducer snippet:
const initialState = { count: 0 };
function counterReducer(state = initialState, action) {
  switch(action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

What will be the state after dispatching { type: 'increment' } twice starting from initial state?
medium
A. { count: 0 }
B. { count: 2 }
C. { count: 1 }
D. { count: -2 }

Solution

  1. Step 1: Analyze reducer behavior for 'increment'

    Each 'increment' action adds 1 to the current count.
  2. Step 2: Apply two increments starting from 0

    0 + 1 = 1 after first increment, then 1 + 1 = 2 after second increment.
  3. Final Answer:

    { count: 2 } -> Option B
  4. Quick Check:

    Two increments = count 2 [OK]
Hint: Add 1 per 'increment' action to count [OK]
Common Mistakes:
  • Counting only one increment
  • Confusing decrement with increment
4. Identify the error in this NgRx reducer code:
function todoReducer(state = [], action) {
  if (action.type === 'add') {
    state.push(action.payload);
    return state;
  }
  return state;
}
medium
A. Mutating state directly instead of returning a new state
B. Missing default case in reducer
C. Incorrect action type string
D. Reducer should not return state

Solution

  1. Step 1: Check state mutation

    The reducer uses state.push(), which changes the original array directly.
  2. Step 2: Understand NgRx immutability rule

    Reducers must return new state objects without mutating the old state.
  3. Final Answer:

    Mutating state directly instead of returning a new state -> Option A
  4. Quick Check:

    Reducers must be pure and immutable [OK]
Hint: Never mutate state; always return new object/array [OK]
Common Mistakes:
  • Using push instead of spread operator
  • Ignoring immutability in reducers
5. You want to create a feature state slice for user profiles using NgRx. Which combination correctly sets up the feature state and selector?
1. Define interface UserProfileState { name: string; age: number; }
2. Create reducer userProfileReducer
3. Register feature state with key 'userProfile'
4. Select user name from store
Which code snippet correctly selects the user name?
hard
A. const selectUserProfile = createFeatureSelector<UserProfileState>('userProfile'); const selectUserName = createSelector(selectUserProfile, state => state.name);
B. const selectUserName = createSelector('userProfile', state => state.name);
C. const selectUserProfile = createSelector('userProfile'); const selectUserName = state => state.name;
D. const selectUserName = createFeatureSelector<UserProfileState>('userProfile').pipe(map(state => state.name));

Solution

  1. Step 1: Understand feature selector usage

    Use createFeatureSelector with the feature key to get the feature state.
  2. Step 2: Create selector for user name

    Use createSelector with the feature selector and a projector function to select the name.
  3. Final Answer:

    const selectUserProfile = createFeatureSelector<UserProfileState>('userProfile'); const selectUserName = createSelector(selectUserProfile, state => state.name); -> Option A
  4. Quick Check:

    Feature selector + createSelector = correct pattern [OK]
Hint: Use createFeatureSelector then createSelector for nested state [OK]
Common Mistakes:
  • Passing string directly to createSelector
  • Using pipe on selector instead of RxJS operators in component