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.
NgRx store concept in Angular
Start learning this pattern below
Jump into concepts and practice - no test required
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.
import { createAction } from '@ngrx/store'; const loadItems = createAction('[Items] Load Items');
import { createReducer, on } from '@ngrx/store'; const initialState = { items: [] }; const itemsReducer = createReducer( initialState, on(loadItems, state => ({ ...state, loading: true })) );
constructor(private store: Store<{ items: any[] }>) {}
this.store.select('items').subscribe(items => console.log(items));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.
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 })
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.
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
NgRx Store in an Angular application?Solution
Step 1: Understand NgRx Store role
The NgRx Store is designed to hold the application state in one place.Step 2: Compare with other options
Options B, C, and D describe other Angular features, not the store's purpose.Final Answer:
To keep all application data in one central place for easy access and updates -> Option CQuick Check:
NgRx Store = Central app data storage [OK]
- Confusing store with routing or HTTP services
- Thinking store manages styles or UI directly
loadItems using NgRx Store in a component?Solution
Step 1: Recall NgRx dispatch syntax
Actions are dispatched by callingthis.store.dispatch(action()).Step 2: Check other options for syntax errors
Options A, B, and C use incorrect methods or assignment instead of dispatch call.Final Answer:
this.store.dispatch(loadItems()); -> Option DQuick Check:
Dispatch action = this.store.dispatch(action()) [OK]
- Using emit or call instead of dispatch
- Assigning dispatch instead of calling it
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?Solution
Step 1: Analyze reducer behavior for 'increment'
Each 'increment' action adds 1 to the current count.Step 2: Apply two increments starting from 0
0 + 1 = 1 after first increment, then 1 + 1 = 2 after second increment.Final Answer:
{ count: 2 } -> Option BQuick Check:
Two increments = count 2 [OK]
- Counting only one increment
- Confusing decrement with increment
function todoReducer(state = [], action) {
if (action.type === 'add') {
state.push(action.payload);
return state;
}
return state;
}Solution
Step 1: Check state mutation
The reducer usesstate.push(), which changes the original array directly.Step 2: Understand NgRx immutability rule
Reducers must return new state objects without mutating the old state.Final Answer:
Mutating state directly instead of returning a new state -> Option AQuick Check:
Reducers must be pure and immutable [OK]
- Using push instead of spread operator
- Ignoring immutability in reducers
1. Define interface UserProfileState { name: string; age: number; }
2. Create reducer userProfileReducer
3. Register feature state with key 'userProfile'
4. Select user name from storeWhich code snippet correctly selects the user name?Solution
Step 1: Understand feature selector usage
UsecreateFeatureSelectorwith the feature key to get the feature state.Step 2: Create selector for user name
UsecreateSelectorwith the feature selector and a projector function to select the name.Final Answer:
const selectUserProfile = createFeatureSelector<UserProfileState>('userProfile'); const selectUserName = createSelector(selectUserProfile, state => state.name); -> Option AQuick Check:
Feature selector + createSelector = correct pattern [OK]
- Passing string directly to createSelector
- Using pipe on selector instead of RxJS operators in component
