Skip to main content

Angular NgRx: A Simple Effect

Greetings!

In this fourth installment of our simple NgRx example series we are going to have a look at effects. Yet again, our example will be very simple.
This is the complete example https://github.com/slmanju/ngrx-at-slmanju/tree/main/ngrx-hello-effects

What we need

  • Store with initial state
  • Create action to add, remove http status codes
  • Create reducers act upon actions
  • Create selector to read from store
  • Create an effect to store http status url
  • Register the reducer
  • Register the effect

Generate the Project

ng new ngrx-hello-effects --routing=false --style=css
cd ngrx-hello-effects/
ng add @ngrx/store@latest
ng add @ngrx/effects@latest
ng serve

Create the Store

At this point i'll asume that you have atleast little idea about the action, reducer and selectors. Hence i'll just add the code. One change i'm doing here to previous examples is I have created separate files.
ngrx-hello-effects/src/app/store/app.state.ts
export interface HttpCodeState {
  code: number,
  url: string
};

export interface AppState {
  statusCodes: HttpCodeState[]
};

export const initialState: HttpCodeState[] = [];
ngrx-hello-effects/src/app/store/app.actions.ts
import { createAction, props } from '@ngrx/store';
import { HttpCodeState } from './app.state';

export const add = createAction('[Http Status] Add', props<{ code : number}>());
export const remove = createAction('[Http Status] Remove', props<{ code : number}>());
export const addUrl = createAction('[Http Status] Add Url', props<HttpCodeState>());
ngrx-hello-effects/src/app/store/app.reducer.ts
import { Action, createReducer, on } from '@ngrx/store';
import { addUrl, remove } from './app.actions';
import { HttpCodeState, initialState } from './app.state';

const _statusCodeReducer = createReducer(
  initialState,
  on(remove, (state, action) => state.filter(http => http.code !== action.code)),
  on(addUrl, (state, action) => [...state, { code: action.code, url: action.url }])
);

export function statusCodeReducer(state: HttpCodeState[] = initialState, action: Action) {
  return _statusCodeReducer(state, action);
}
ngrx-hello-effects/src/app/store/app.selector.ts
import { AppState } from "./app.state";

export const selectCodes = (state: AppState) => state.statusCodes;
Now let's create our effect.

Create the Effect

In a real-world application we need to fetch data from our backend. What we have previousely done is storing objects in browser(sort of). We need somewhere to actually save, retrieve those values from our server back end. In NgRx world, Effects are responsible for those kind of things.

Effects can capture the action dispatched from a component, call the backend service, modify the response or handle errors if needed and then dispatch new action with new data. Our reducers can capture this new action.

To create an effect, there is another createXXX function provided by NgRx. For the simplicity, we are going to fake this using a timer delay.

ngrx-hello-effects/src/app/store/app.effects.ts
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { timer } from 'rxjs';
import { map, mergeMap } from 'rxjs/operators';
import { add, addUrl } from './app.actions';

@Injectable()
export class HttpUrlEffects {

  constructor(private actions$: Actions) {}

  readonly addUrl = createEffect(() => 
    this.actions$.pipe(
      ofType(add),
      mergeMap((action) => {
        return timer(10)
                .pipe(
                  map(() => addUrl({ code: action.code, url: 'https://http.cat/' + action.code }))
                );
      })
    ));
}
Here we are responding to the actions of type add then dispatch another action named addUrl.

Just like reducers, we need to register effects in our app.module
EffectsModule.forRoot([ HttpUrlEffects ])
There is nothing special about the component though below is the code for the completions.
ngrx-hello-effects/src/app/app.component.ts
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { add, remove } from './store/app.actions';
import { selectCodes } from './store/app.selector';
import { AppState, HttpCodeState } from './store/app.state';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  code$: Observable<HttpCodeState[]>;
  statusCode: number;
  
  constructor(private httpStatusStore: Store<AppState>) {
    this.code$ = httpStatusStore.select(selectCodes);
  }

  onAdd() {
    this.httpStatusStore.dispatch(add({ code: this.statusCode }));
  }

  onRemove() {
    this.httpStatusStore.dispatch(remove({ code: this.statusCode }));
  }

}
That is it! It is not that hard. Get the full code from here https://github.com/slmanju/ngrx-at-slmanju/tree/main/ngrx-hello-effects and have fun.

Happy coding ;)