DevTool
DevTool

Reputation: 319

Angular NgRx effect not fire

I start learn to work with NgRx library and after I learn the basic of how to save state with dispatch and get the state with the store, I move to effect and try to understand and implement it but its not fire.

user.model

export interface User {
  userID: number;
  id: number;
  title: string;
  completed: boolean;
}

user.action

import { Action } from '@ngrx/store';
import { User } from '../models/user.model';

export const LOAD_USER = '[USER] Load';

export class LoadUser implements Action {
  readonly type = LOAD_USER;

  constructor(public payload: User) {}
}

export type Action = LoadUser;

user.reducer

import { User } from '../models/user.model';
import * as UserAction from '../actions/user.actions';

const initialState: User = {
  userID: null,
  id: null,
  title: null,
  completed: null
};

export function userReducer(state: User = initialState, action: UserAction.Action) {
  switch (action.type) {
    case UserAction.LOAD_USER:
      return action.payload;
    default:
      return state;
  }

}

app.state

import { Comment } from './models/comment.model';
import { User } from './models/user.model';


export interface AppState {
  readonly comment: Comment[];
  readonly counter: number;
  readonly user: User[];

}

app.module

  imports: [
    BrowserModule,
    HttpClientModule,
    AppRoutingModule,
    FormsModule,
    ReactiveFormsModule,
    StoreModule.forRoot({
      comment: commentReducer,
      counter: counterReducer,
      user: userReducer
    }),
    EffectsModule.forRoot([UserEffect]),
    StoreDevtoolsModule.instrument({
      maxAge: 25, // Retains last 25 states
    }),
  ],

user.efect

import { Injectable } from '@angular/core';
import { Actions, ofType, createEffect } from '@ngrx/effects';
import { EMPTY } from 'rxjs';
import { map, mergeMap, catchError } from 'rxjs/operators';
import { UserService } from '../core/services/user.service';
import * as UserAction from '../actions/user.actions';


@Injectable()
export class UserEffect {
  $loadUser = createEffect(() => this.actions$.pipe(
    ofType(UserAction.LOAD_USER),
    mergeMap(() => this.userService.getUsers().pipe(
      map((user) => new UserAction.LoadUser({userID: user[0].userID, id: user[0].id, title: user[0].title, completed: user[0].completed}))
    )),
    catchError(() => EMPTY)
  ));

  constructor(
    private actions$: Actions,
    private userService: UserService
  ) {
  }
}

user.service:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class UserService {

  constructor(private http: HttpClient) { }

  getUsers() {
    return this.http.get('https://jsonplaceholder.typicode.com/todos/1');
  }
}

I look in chrome dev-tool and it seems that the event not fire. What the problem? thanks

When i try to call the action/dispatch from component But isn't that job done with the effect? with parameter i need to input ?

  ngOnInit() {
    this.store.dispatch(new UserActions.LoadUser({ userID: null, id: null, title: null, completed: null }))
  }

Upvotes: 0

Views: 628

Answers (1)

Miroslav Jonas
Miroslav Jonas

Reputation: 6637

You would need two different actions, one LOAD_USER to trigger the load and second e.g. USER_LOADED to carry the user information from service and save it in the reducer.

This is how your actions would look like:

export const LOAD_USER = '[USER] Load';
export const USER_LOADED = '[USER] Loaded';

export class LoadUser implements Action {
  readonly type = LOAD_USER; // no payload => no constructor needed
}

export class UserLoaded implements Action {
  readonly type = USER_LOADED;

  constructor(public payload: User) {}
}

your reducer reacts to both actions:

export function userReducer(state: User = initialState, action: UserAction.Action) {
  switch (action.type) {
    case UserAction.LOAD_USER:
      return initialState; // reset user to default value while loading
    case UserAction.USER_LOADED:
      return action.payload;
    default:
      return state;
  }
}

and your effect slightly different:

$loadUser = createEffect(() => this.actions$.pipe(
    ofType(UserAction.LOAD_USER),
    mergeMap(() => this.userService.getUsers().pipe(
      map(user => new UserAction.UserLoaded(user[0]))
    )),
    catchError(() => EMPTY)
  ));

The way your effect was written can cause memory leak actually, while you are creating endless loop - when LoadUser is fired, you fetch user and fire LoadUser, which triggers the effect again...

Upvotes: 0

Related Questions