Angular Tutorial: Store, Actions, Reducers 🎯

beginner
24 min

Angular Tutorial: Store, Actions, Reducers 🎯

Welcome to our comprehensive guide on Angular's Store, Actions, and Reducers! In this lesson, we will dive deep into these essential concepts, making them easy to understand for beginners while providing enough depth for intermediate learners.

What are Store, Actions, and Reducers? 📝

In Angular applications, the Store is a central data structure for managing application state. Actions are events triggered by user interactions or system events, and Reducers are pure functions responsible for updating the state based on the dispatched actions.

Let's take a closer look at each component:

Store 💡

The Store in Angular is implemented using NgRx, a popular library for managing application state. It provides a centralized place for managing shared state across components, services, and other parts of the application.

Actions 💡

Actions are events that trigger the Reducer to update the state. They represent user interactions or system events that lead to changes in the application state.

Reducers 💡

Reducers are pure functions that handle the changes to the state based on dispatched actions. They take the current state and an action as input and return the updated state.

Creating a Simple Store, Actions, and Reducers Example 📝

To illustrate how Store, Actions, and Reducers work together, let's create a simple example application that manages a list of todos.

Prerequisites 📝

  • Angular CLI
  • NgRx

To install NgRx, run the following command:

bash
npm install @ngrx/store @ngrx/effects --save

Setting Up the Store, Actions, and Reducers 💡

  1. Create a todos module:
bash
ng generate module todos --route todos
  1. Inside the todos.module.ts, import the StoreModule and NgReducerStoreModule:
typescript
import { StoreModule } from '@ngrx/store'; import { NgReduxStoreModule } from '@ngrx/store'; @NgModule({ imports: [ StoreModule.forFeature('todos', reducers), NgReduxStoreModule ] }) export class TodosModule { }
  1. Create a todos.actions.ts file and define the initial actions:
typescript
import { createAction, props } from '@ngrx/store'; export const LoadTodos = createAction('[Todos] Load Todos'); export const LoadTodosSuccess = createAction( '[Todos] Load Todos Success', props<{ todos: any[] }>() ); export const LoadTodosFailure = createAction( '[Todos] Load Todos Failure', props<{ error: any }>() );
  1. Create a todos.reducers.ts file and define the initial state and reducer:
typescript
import { createReducer, on } from '@ngrx/store'; import * as TodosActions from './todos.actions'; export const initialState: any = { todos: [], loading: false, error: null }; export const todosReducer = createReducer( initialState, on(TodosActions.LoadTodos, state => ({ ...state, loading: true })), on(TodosActions.LoadTodosSuccess, (state, { todos }) => ({ ...state, loading: false, todos })), on(TodosActions.LoadTodosFailure, (state, { error }) => ({ ...state, loading: false, error })) );
  1. Now, let's create a simple todos.service.ts to fetch and store the todos data:
typescript
import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { TodosActions, TodosActionTypes } from './todos.actions'; import { catchError, map, mergeMap } from 'rxjs/operators'; @Injectable() export class TodosEffects { loadTodos$ = createEffect(() => this.actions$.pipe( ofType(TodosActionTypes.LoadTodos), mergeMap(() => this.todosService.getTodos().pipe( map(todos => TodosActions.LoadTodosSuccess({ todos })), catchError(error => of(TodosActions.LoadTodosFailure({ error }))) )) ) ); constructor( private actions$: Actions, private todosService: TodosService ) {} }
  1. Finally, register the Effects in the todos.module.ts:
typescript
import { StoreModule } from '@ngrx/store'; import { NgReduxStoreModule } from '@ngrx/store'; import { EffectsModule } from '@ngrx/effects'; import { TodosEffects } from './todos.effects'; @NgModule({ imports: [ StoreModule.forFeature('todos', reducers), NgReduxStoreModule, EffectsModule.forFeature([TodosEffects]) ] }) export class TodosModule { }

Using the Store, Actions, and Reducers in Components 💡

Now that we have set up the Store, Actions, and Reducers, we can use them in our components to manage the todos data.

typescript
import { Component, OnInit } from '@angular/core'; import { Store, select } from '@ngrx/store'; import * as TodosActions from './todos.actions'; @Component({ selector: 'app-todos', templateUrl: './todos.component.html', styleUrls: ['./todos.component.css'] }) export class TodosComponent implements OnInit { todos$ = this.store.pipe(select('todos')); constructor(private store: Store) { } ngOnInit(): void { this.store.dispatch(TodosActions.LoadTodos); } }

Quiz 💡

Question: Which Angular library manages the Store in the provided example?

A: NgRx B: Angular CLI C: NgStore Correct: A Explanation: NgRx is the library that manages the Store in the provided example.


That's it for our first lesson on Angular's Store, Actions, and Reducers! In the next lessons, we'll dive deeper into more advanced topics and best practices. Stay tuned! 🎯