0
0
Angularframework~5 mins

Actions and reducers pattern in Angular

Choose your learning style9 modes available
Introduction

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.

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.