The actions and reducers pattern helps manage app data in a clear way. It keeps track of changes step-by-step so your app stays organized and easy to fix.
0
0
Actions and reducers pattern in Angular
Introduction
When you want to keep track of user actions like clicks or form submissions.
When your app has data that changes over time and you want to update the screen smoothly.
When you want to separate how data changes from how the app shows data.
When you want to make your app easier to test and debug by following clear rules.
Syntax
Angular
import { createAction, props } from '@ngrx/store'; export const actionName = createAction( '[Source] Event', props<{ payloadProperty: any }>() ); import { createReducer, on } from '@ngrx/store'; export const initialState = { /* initial state object */ }; export const reducerName = createReducer( initialState, on(actionName, (state, { payloadProperty }) => ({ ...state, /* new state changes */ })) );
Actions describe what happened, carrying any needed data.
Reducers decide how the state changes based on actions.
Examples
This action has no extra data. It just signals to increase a counter.
Angular
import { createAction } from '@ngrx/store'; export const increment = createAction('[Counter] Increment');
This action carries a string item to add to a list.
Angular
import { createAction, props } from '@ngrx/store'; export const addItem = createAction( '[Todo] Add Item', props<{ item: string }>() );
This reducer increases the count by 1 when the increment action happens.
Angular
import { createReducer, on } from '@ngrx/store'; export const initialState = { count: 0 }; export const counterReducer = createReducer( initialState, on(increment, state => ({ ...state, count: state.count + 1 })) );
Sample Program
This Angular component shows a number and a button. When you click the button, it sends an increment action. The reducer updates the count by 1. The screen updates to show the new count.
This pattern keeps the data changes clear and separate from the display.
Angular
import { Component } from '@angular/core'; import { Store, createAction, createReducer, on, props } from '@ngrx/store'; import { Observable } from 'rxjs'; // Define action export const increment = createAction('[Counter] Increment'); // Define state interface interface CounterState { count: number; } // Initial state const initialState: CounterState = { count: 0 }; // Reducer function export const counterReducer = createReducer( initialState, on(increment, state => ({ ...state, count: state.count + 1 })) ); @Component({ selector: 'app-counter', template: ` <main> <h1>Counter: {{ count$ | async }}</h1> <button (click)="incrementCount()" aria-label="Increment counter">Increment</button> </main> `, standalone: true }) export class CounterComponent { count$: Observable<number>; constructor(private store: Store<{ counter: CounterState }>) { this.count$ = store.select(state => state.counter.count); } incrementCount() { this.store.dispatch(increment()); } }
OutputSuccess
Important Notes
Always keep actions simple and focused on one event.
Reducers must not change the old state directly; always return a new state object.
Use descriptive action names to understand what happened easily.
Summary
Actions tell what happened, carrying needed data.
Reducers decide how state changes based on actions.
This pattern helps keep app data organized and easy to manage.