Deep Dive into NgRx
Introduction
NgRx is a set of reactive libraries for Angular, inspired by Redux, that provide a comprehensive approach to state management. NgRx uses the power of RxJS to manage state and side effects, ensuring a predictable state management pattern through a unidirectional data flow. This makes state changes traceable and debuggable, crucial for complex applications.
Core Concepts of NgRx
-
Store:
- The store is a single source of truth for the application state. It holds the state and serves as the central repository from which components can select and manage state.
-
Actions:
- Actions are dispatched by components or services to trigger state changes. They are plain JavaScript objects that describe an event in the application.
- Example:
export const loadItems = createAction('[Item List] Load Items'); export const loadItemsSuccess = createAction( '[Item List] Load Items Success', props<{ items: Item[] }>() );
-
Reducers:
- Reducers are pure functions that define how the state changes in response to actions. They take the current state and an action as inputs and return a new state.
- Example:
const initialState: State = { items: [] }; const itemReducer = createReducer( initialState, on(loadItemsSuccess, (state, { items }) => ({ ...state, items })) );
-
Selectors:
- Selectors are pure functions used to query slices of state from the store. They provide a way to access and derive data from the store in a reusable manner.
- Example:
export const selectItems = createSelector( (state: AppState) => state.items, (items) => items );
-
Effects:
- Effects handle side effects in NgRx, such as HTTP requests or interactions with external APIs. They listen for actions dispatched to the store and perform asynchronous operations.
- Example:
@Injectable() export class ItemEffects { loadItems$ = createEffect(() => this.actions$.pipe( ofType(loadItems), mergeMap(() => this.itemService.getAll() .pipe( map(items => loadItemsSuccess({ items })), catchError(() => EMPTY) )) ) ); constructor( private actions$: Actions, private itemService: ItemService ) {} }
State Management Lifecycle in NgRx
The state management lifecycle in NgRx follows a unidirectional data flow:
-
Component:
- A component dispatches an action to initiate a state change.
- Example:
this.store.dispatch(loadItems());
-
Action:
- The dispatched action is caught by the store, which forwards it to the reducer or effect.
- Example:
export const loadItems = createAction('[Item List] Load Items');
-
Reducer:
- If the action affects the state directly, the reducer processes it and returns a new state.
- Example:
const itemReducer = createReducer( initialState, on(loadItemsSuccess, (state, { items }) => ({ ...state, items })) );
-
Effect:
- If the action involves a side effect (like an API call), the effect handles it and may dispatch further actions based on the result.
- Example:
@Injectable() export class ItemEffects { loadItems$ = createEffect(() => this.actions$.pipe( ofType(loadItems), mergeMap(() => this.itemService.getAll() .pipe( map(items => loadItemsSuccess({ items })), catchError(() => EMPTY) )) ) ); }
-
Store:
- The store holds the updated state and makes it available to components via selectors.
- Example:
this.items$ = this.store.select(selectItems);
-
Selector:
- Components use selectors to retrieve data from the store.
- Example:
export const selectItems = createSelector( (state: AppState) => state.items, (items) => items );
NgRx State Management Lifecycle

Comparison with React
-
State Management:
- NgRx: Provides a structured and reactive approach to state management using RxJS. It ensures a unidirectional data flow and predictable state changes.
- Redux (React): Uses a similar pattern with actions, reducers, and a store. React’s Redux can also be enhanced with middleware like
redux-thunkorredux-sagafor handling side effects.
-
Side Effects:
- NgRx: Uses effects to handle asynchronous operations and side effects, leveraging the power of RxJS.
- Redux (React): Handles side effects using middleware like
redux-thunkfor simple async logic orredux-sagafor more complex scenarios.
-
Selectors:
- NgRx: Provides a built-in way to create selectors for querying the store’s state.
- Redux (React): Uses libraries like
reselectto create memoized selectors.
-
Integration with Angular:
- NgRx: Seamlessly integrates with Angular’s dependency injection and change detection mechanisms.
- Redux (React): Uses React-Redux for integration, connecting React components to the Redux store.
Summary
NgRx offers a robust and reactive approach to state management in Angular applications, leveraging the power of RxJS to handle state and side effects in a predictable manner. By following a unidirectional data flow and using actions, reducers, selectors, and effects, NgRx ensures that state changes are traceable and manageable. This makes it an excellent choice for complex applications requiring scalable and maintainable state management solutions.