Angular NgRx Actions, Reducers & Effects

Learn NgRx Actions, Reducers, and Effects with architecture diagrams, real-world examples, Signals integration, best practices, common mistakes, and interview questions for Angular 20.

NgRx follows the Redux architecture, where every state change flows through a predictable pipeline.

Instead of components directly modifying application state, NgRx introduces three important building blocks:

  • Actions → Describe what happened.
  • Reducers → Update application state.
  • Effects → Handle asynchronous operations.

Together, these components provide a scalable, testable, and maintainable architecture for enterprise Angular applications.


Table of Contents

  • What are Actions?
  • What are Reducers?
  • What are Effects?
  • NgRx Data Flow
  • Creating Actions
  • Creating Reducers
  • Creating Effects
  • Success & Failure Actions
  • Signals Integration
  • Enterprise Banking Example
  • Best Practices
  • Common Mistakes
  • Top 10 Interview Questions
  • Summary

NgRx Architecture

flowchart LR

Component

Component --> DispatchAction

DispatchAction --> Action

Action --> Effect

Effect --> API

API --> SuccessAction

SuccessAction --> Reducer

Reducer --> Store

Store --> Selector

Selector --> Component

What are Actions?

An Action represents an event that occurred inside the application.

Examples

  • User logged in
  • Customer loaded
  • Product added
  • Order cancelled
  • Transaction completed

Actions describe what happened, not how it should happen.


Action Characteristics

  • Immutable
  • Serializable
  • Simple objects
  • No business logic
  • Easy to debug

Creating an Action

import { createAction } from '@ngrx/store';

export const loadCustomers = createAction(
  '[Customer] Load Customers'
);

Action with Payload

import { createAction, props } from '@ngrx/store';

export const addCustomer = createAction(
  '[Customer] Add Customer',
  props<{ customer: Customer }>()
);

Payload carries additional data.


Dispatching an Action

this.store.dispatch(loadCustomers());

With payload

this.store.dispatch(
  addCustomer({
    customer: newCustomer
  })
);

Action Flow

flowchart LR

ButtonClick

ButtonClick --> Dispatch

Dispatch --> Action

Action --> Effect

What is a Reducer?

A Reducer is a pure function responsible for updating application state.

Reducers receive:

  • Current State
  • Action

Reducers return:

  • New State

Reducers never modify the existing state.


Reducer Architecture

flowchart LR

Action --> Reducer

Reducer --> NewState

NewState --> Store

Reducer Example

export interface CustomerState {

  customers: Customer[];

  loading: boolean;

}

export const initialState: CustomerState = {

  customers: [],

  loading: false

};

Reducer

export const customerReducer = createReducer(

  initialState,

  on(loadCustomers, state => ({

    ...state,

    loading: true

  })),

  on(loadCustomersSuccess, (state, { customers }) => ({

    ...state,

    customers,

    loading: false

  }))

);

Why Reducers Must Be Pure

Reducers should:

  • Return new state
  • Never call APIs
  • Never modify existing objects
  • Never perform asynchronous work

Pure reducers make state changes predictable and easy to test.


Immutable Updates

❌ Wrong

state.customers.push(customer);

✅ Correct

return {

  ...state,

  customers: [

    ...state.customers,

    customer

  ]

};

What are Effects?

Effects handle:

  • HTTP requests
  • Authentication
  • File uploads
  • Logging
  • Notifications
  • Navigation

Effects keep reducers pure.


Effect Architecture

flowchart LR

Action

Action --> Effect

Effect --> RESTAPI

RESTAPI --> SuccessAction

RESTAPI --> FailureAction

SuccessAction --> Reducer

FailureAction --> Reducer

Creating an Effect

@Injectable()
export class CustomerEffects {

  loadCustomers$ = createEffect(() =>

    this.actions$.pipe(

      ofType(loadCustomers),

      switchMap(() =>

        this.customerService.getCustomers().pipe(

          map(customers =>

            loadCustomersSuccess({

              customers

            })

          ),

          catchError(error =>

            of(

              loadCustomersFailure({

                error

              })

            )

          )

        )

      )

    )

  );

  constructor(

    private actions$: Actions,

    private customerService: CustomerService

  ) {}

}

Success and Failure Actions

Success

export const loadCustomersSuccess =

createAction(

'[Customer] Load Success',

props<{

customers: Customer[]

}>()

);

Failure

export const loadCustomersFailure =

createAction(

'[Customer] Load Failure',

props<{

error: unknown

}>()

);

Complete Flow

sequenceDiagram

participant User

participant Component

participant Store

participant Effect

participant API

participant Reducer

User->>Component: Click Load

Component->>Store: Dispatch Action

Store->>Effect: Action Observed

Effect->>API: HTTP Request

API-->>Effect: Response

Effect->>Store: Success Action

Store->>Reducer: Update State

Reducer-->>Component: Updated Data

Multiple Effects

Applications often have multiple Effects.

Examples

  • Authentication
  • Customers
  • Orders
  • Products
  • Payments
  • Notifications

Each feature should own its own Effects.


Signals Integration

Selectors can be converted into Signals.

customers = toSignal(

this.store.select(

selectCustomers

)

);

Template

@for (

customer of customers();

track customer.id

) {

<div>

{{ customer.name }}

</div>

}

Signals improve template readability while NgRx manages global state.


Enterprise Banking Example

Customer Dashboard

Features

  • Authentication
  • Accounts
  • Loans
  • Cards
  • Investments
  • Transactions

Architecture

flowchart TD

Dashboard

Dashboard --> DispatchAction

DispatchAction --> CustomerEffect

CustomerEffect --> BankingAPI

BankingAPI --> SuccessAction

SuccessAction --> Reducer

Reducer --> Store

Store --> Signals

Signals --> Dashboard

Benefits

  • Predictable workflow
  • Centralized state
  • Easier debugging
  • Better scalability
  • Cleaner architecture

Folder Structure

store/

├── actions/
│   ├── customer.actions.ts
│   ├── auth.actions.ts
│
├── reducers/
│   ├── customer.reducer.ts
│
├── effects/
│   ├── customer.effects.ts
│
├── selectors/
│   ├── customer.selectors.ts
│
└── models/

Performance Best Practices

Practice Benefit
Keep Actions simple Better readability
Keep Reducers pure Predictable updates
Handle APIs in Effects Separation of concerns
Use immutable state Efficient change detection
Split features Better scalability
Convert Selectors to Signals Cleaner templates
Use OnPush Faster rendering
Use DevTools Easier debugging

Common Mistakes

Calling APIs in Reducers

❌ Wrong

Reducers should never perform asynchronous operations.


Business Logic Inside Actions

Actions only describe events.

Keep business logic in:

  • Effects
  • Services

Mutating State

❌ Wrong

state.loading = true;

Always return a new state object.


One Huge Reducer

Split reducers by feature.

Examples

  • Auth
  • Orders
  • Customers
  • Products

Ignoring Failure Actions

Always create both:

  • Success Actions
  • Failure Actions

This enables proper error handling and improves user experience.


Actions vs Reducers vs Effects

Feature Actions Reducers Effects
Describe Events
Update State
Call APIs
Pure Function N/A
Async Work
Dispatch Actions

Advantages

Component Benefit
Actions Predictable event model
Reducers Immutable state updates
Effects Clean async workflows
Store Single source of truth
Signals Reactive UI
DevTools Time-travel debugging

Top 10 Actions, Reducers & Effects Interview Questions

1. What is an Action in NgRx?

Answer

An Action is an immutable object that represents an event occurring in the application, such as loading data or submitting a form.


2. What is a Reducer?

Answer

A Reducer is a pure function that receives the current state and an Action, then returns a new immutable state.


3. Why must Reducers be pure?

Answer

Pure reducers always produce the same output for the same input, making state changes predictable, testable, and easy to debug.


4. What are Effects?

Answer

Effects handle asynchronous operations such as HTTP requests, authentication, navigation, logging, and notifications without polluting reducers.


5. Why shouldn't APIs be called inside Reducers?

Answer

Reducers must remain synchronous and side-effect free. API calls belong in Effects because they involve asynchronous operations.


6. What is the complete NgRx flow?

Answer

Component → Action → Effect (optional) → Success/Failure Action → Reducer → Store → Selector → Component


7. Why are Success and Failure Actions important?

Answer

They allow applications to update loading states, display errors, retry failed requests, and keep asynchronous workflows predictable.


8. How do Signals integrate with NgRx?

Answer

Selectors can be converted into Signals using toSignal(), allowing Angular templates to consume Store data with Signal-based reactivity.


9. What are common mistakes when using Actions, Reducers, and Effects?

Answer

  • Calling APIs inside reducers
  • Mutating state
  • Adding business logic to actions
  • Creating oversized reducers
  • Ignoring failure actions

Answer

Use feature-based Actions, Reducers, Effects, and Selectors together with immutable state, NgRx DevTools, Signals for UI consumption, and OnPush Change Detection for scalable enterprise applications.


Interview Cheat Sheet

Requirement Recommended Solution
User Events Actions
State Updates Reducers
HTTP Requests Effects
Global State Store
Read State Selectors
UI Reactivity Signals
Change Detection OnPush
Error Handling Failure Actions
Debugging NgRx DevTools
Enterprise Apps Feature-based NgRx Architecture

Summary

Actions, Reducers, and Effects form the core of NgRx's predictable state management architecture. Actions describe events, Reducers update immutable state, and Effects manage asynchronous work such as API communication. This separation of responsibilities results in applications that are easier to understand, test, debug, and scale. Modern Angular applications frequently combine NgRx for global state with Signals for reactive UI consumption, delivering a clean and high-performance architecture.


Key Takeaways

  • ✔ Actions describe what happened.
  • ✔ Reducers are pure functions that return new immutable state.
  • ✔ Effects handle asynchronous operations and side effects.
  • ✔ Components dispatch Actions instead of changing state directly.
  • ✔ Always keep Reducers synchronous and side-effect free.
  • ✔ Use Success and Failure Actions for robust async workflows.
  • ✔ Convert Selectors into Signals for cleaner templates.
  • ✔ Organize NgRx by feature for better scalability.
  • ✔ Use OnPush and immutable updates for optimal performance.
  • ✔ Actions, Reducers, and Effects are among the most frequently asked Angular interview topics.

Next Article

➡️ 99-NgRx-Selectors-QA.md