← Back to Chapters

NgRx Actions, Reducers & Selectors

⚡ NgRx Actions, Reducers & Selectors

NgRx State Management Angular

? Quick Overview

In NgRx, Actions, Reducers, and Selectors are the core building blocks of state management. They define how events are dispatched, how state changes occur, and how components can access data efficiently and predictably.

? Key Concepts

  • Actions – Plain objects that describe what happened in the application.
  • Reducers – Pure functions that take the current state and an action, then return a new state.
  • Selectors – Reusable functions that know how to read specific slices of the state.
  • Store – A single, centralized state container that holds the application state tree.

? Syntax & Theory

NgRx builds on the Redux pattern and provides utilities such as createAction, createReducer, and createSelector to make state management easier and more type-safe.

  • Actions usually live in *.actions.ts files and represent events like [Counter] Increment or [Auth] Login Success.
  • Reducers are defined with createReducer and on to map actions to state transitions.
  • Selectors are composed from feature selectors (like createFeatureSelector()) and are reused across components.

? Code Examples

⚙️ Actions – Describe What Happened

Actions are simple objects with a type (and optionally a payload). In NgRx we create them using createAction.

? View Code Example
// counter.actions.ts - define counter-related events
import { createAction } from '@ngrx/store';

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

⚙️ Reducers – Decide How State Changes

Reducers are pure functions: given the previous state and an action, they return the next state without mutating the original one.

? View Code Example
// counter.reducer.ts - update state based on actions
import { createReducer, on } from '@ngrx/store';
import { increment, decrement, reset } from './counter.actions';

export const initialState = 0;

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

⚙️ Selectors – Read Data from the Store

Selectors encapsulate how to get specific pieces of state. Components subscribe to selectors instead of directly accessing the store shape.

? View Code Example
// counter.selectors.ts - expose reusable state readers
import { createSelector, createFeatureSelector } from '@ngrx/store';

export const selectCounterState = createFeatureSelector<number>('count');

export const selectCount = createSelector(
  selectCounterState,
  (state: number) => state
);

? Live Flow & Explanation

? How It All Works Together

  1. User clicks a button in the UI (e.g., "Increment").
  2. The component dispatches the increment() action to the store.
  3. The counterReducer receives the current state and the action, and returns state + 1.
  4. The store updates its internal state and notifies subscribers.
  5. The component uses the selectCount selector to read the latest value and updates the view.

Because reducers are pure and selectors are reusable, NgRx makes your state management predictable, testable, and easy to reason about.

✅ Tips & Best Practices

  • Keep action types descriptive (e.g., [Auth] Login Success instead of LOGIN_OK).
  • Always keep reducers pure — no API calls, randomness, or side effects.
  • Use selectors everywhere instead of reading raw state shape in components.
  • Group related actions, reducers, and selectors by feature (e.g., counter, auth).
  • Write unit tests for reducers and selectors to catch regressions early.

? Try It Yourself

  • Create increment, decrement, and reset actions for a counter app.
  • Implement a reducer that:
    • Starts from an initial count of 0.
    • Increments and decrements correctly based on the dispatched action.
    • Resets the count back to 0 on reset.
  • Define a selector selectCount and use it in a component to display the current count.
  • Extend the state to include a step value and update your actions, reducer, and selector to support dynamic increments.