Мой эффект отправил неверное действие или бесконечный цикл - PullRequest
0 голосов
/ 06 октября 2019

Я новичок в ngrx, и я надеюсь, что кто-то может быть добрым и помочь мне, потому что я прочитал много тем об этой проблеме, и я все еще в том же духе. Я тестирую @ngrx/effects, и я просто хочу вернуть простой массив ролей пользователей в начале приложения (просто чтобы понять, как это работает на данный момент). Когда я использую этот код ниже:

 ...
 ...
 switchMap(() => this.roleService.getAllRoles().pipe(
   map(roles => {
     return new AllRolesLoaded({ roles });
   }),
   catchError((error) => of(new ErrorLoadRole(error)))
 )),
// I got infinite loop

я получил бесконечный цикл. С моим исследованием стека потока я обнаружил, что я должен использовать .tap() вместо .map(). Но когда я использую .tap():

 ...
 ...
 switchMap(() => this.roleService.getAllRoles().pipe(
   tap(roles => {
     return new AllRolesLoaded({ roles });
   }),
   catchError((error) => of(new ErrorLoadRole(error)))
 )),

// I got this:
// core.js:6014 ERROR Error: Effect "RoleEffects.loadRoles$" dispatched an invalid action:   ...
// core.js:6014 ERROR TypeError: Actions must have a type property

Для недопустимого действия я попытался:
- Эффект отправил недопустимое действие:
- версия с декоратором @Effect (ngrx 8.0.1), версия createEffect() (ngrx 8.3.0).
- swithMap() mergeMap() exhaustMap() ect ... и у меня есть результат sama.

Здесь полный код, может быть, вы можете понять, что здесь происходит.

//  #actions/role.actions.ts

// ngrx version 8.0.1
export enum RoleActionTypes {
  RequestLoadRoles = '[Role] Request Load data Roles',
  AllRolesLoaded = '[Roles API] All Roles Loaded',
  ErrorLoadRole = '[Roles API] Error load roles',
}

export class RequestLoadRoles implements Action {
  readonly type = RoleActionTypes.RequestLoadRoles;
}
export class AllRolesLoaded implements Action {
  readonly type = RoleActionTypes.AllRolesLoaded;
  constructor(public payload: { roles: Role[] }) {}
}
export class ErrorLoadRole {
  readonly type = RoleActionTypes.ErrorLoadRole;
  constructor(public payload: HttpErrorResponse) {}
}

// ngrx version 8.3.0 but i cannot handle it yet
// export const RequestLoadRoles = createAction('[Role] Request Load data Roles');
// export const AllRolesLoaded = createAction('[Roles API] All Roles Loaded', props<{ role: Role[] }>());

 export type RoleActions =
   | RequestLoadRoles
   | AllRolesLoaded
   | ErrorLoadRole
   ;

//  #effects/role.effect.ts

@Injectable()
export class RoleEffects {

// ngrx version 8.0.1
@Effect()
loadRoles$ = this.actions$.pipe(
  ofType<RequestLoadRoles>(RoleActionTypes.RequestLoadRoles),
  withLatestFrom(this.store.pipe(select(roles => roles.rolesLoaded))),
  filter(([action, loaded]) => !loaded),
  switchMap(() => this.roleService.getAllRoles().pipe(
    tap(roles => { // with .tap() i got the invalid dispatched action, i dont know why, and .map() give my an infinite loop
      return new AllRolesLoaded({ roles });
    }),
    catchError((error) => of(new ErrorLoadRole(error)))
  )),
);

// ngrx version 8.3.0 but i cannot handle it yet
// loadRoles$ = createEffect(() => this.actions$.pipe(
//   ofType('[Roles API] All Roles Loaded'),
//   mergeMap(() => this.roleService.getAllRoles()
//     .pipe(
//       map(roles => ({ type: '[Roles API] All Roles Loaded', payload: roles })),
//       catchError(() => EMPTY)
//     ))
//   )
// );

@Effect()
init$: Observable<RoleActions> = defer(() => {
  return of(new RequestLoadRoles());
});
constructor(private actions$: Actions, private store: Store<RolesStateEntity>, private roleService: RoleService) {}

}

# reducer/role.reducer.ts
export const roleFeatureKey = 'role';

export interface RolesStateEntity {
  rolesLoaded: boolean;
  rolesLoading: boolean;
  queryResult: Role[];
}

export const initialRolesState: RolesStateEntity = {
  rolesLoaded: false,
  rolesLoading: false,
  queryResult: []
};


export function roleReducer(state = initialRolesState, action: RoleActions): RolesStateEntity {
  switch (action.type) {
    case RoleActionTypes.AllRolesLoaded:
      return {
        ...state,
        queryResult: action.payload.roles,
        rolesLoading: false,
        rolesLoaded: true,
     };
    case RoleActionTypes.ErrorLoadRole:
     return {
        ...state,
        rolesLoading: false,
     };
    default:
      return state;
  }
}

  # service/role.service.ts there is only 1 method
  getAllRoles(): Observable<Role[]> {
    return new Observable(observer => {
      const array = [
        {
          name: 'ADMIN',
          permissions: ['fullAccessUserManagement', 'canDeleteUserManagement', 'canUpdateUserManagement', 'canReadUserManagement'],
        },
        {
          name: 'MODERATOR',
          permissions: ['canDeleteUserManagement', 'canUpdateUserManagement', 'canReadUserManagement'],
        },
        {
          name: 'USER',
          permissions: ['canReadUserManagement'],
        },
        {
          name: 'GUEST',
          permissions: [],
        },
      ]
     observer.next(array);
    });
  }

Здесь полная ошибка консоли:

core.js:6014 ERROR Error: Effect "RoleEffects.loadRoles$" dispatched an invalid action: [{"name":"ADMIN","permissions":["fullAccessUserManagement","canDeleteUserManagement","canUpdateUserManagement","canReadUserManagement"]},{"name":"MODERATOR","permissions":["canDeleteUserManagement","canUpdateUserManagement","canReadUserManagement"]},{"name":"USER","permissions":["canReadUserManagement"]},{"name":"GUEST","permissions":[]}]
at reportInvalidActions (effects.js:338)
at MapSubscriber.project (effects.js:428)
at MapSubscriber._next (map.js:29)
at MapSubscriber.next (Subscriber.js:49)
at ExhaustMapSubscriber.notifyNext (exhaustMap.js:60)
at InnerSubscriber._next (InnerSubscriber.js:11)
at InnerSubscriber.next (Subscriber.js:49)
at MergeMapSubscriber.notifyNext (mergeMap.js:69)
at InnerSubscriber._next (InnerSubscriber.js:11)
at InnerSubscriber.next (Subscriber.js:49)
...
Show 139 more frames
core.js:6014 ERROR TypeError: Actions must have a type property
at ActionsSubject.next (store.js:168)
at Store.next (store.js:709)
at SafeSubscriber.__tryOrUnsub (Subscriber.js:183)
at SafeSubscriber.next (Subscriber.js:122)
at Subscriber._next (Subscriber.js:72)
at Subscriber.next (Subscriber.js:49)
at MergeMapSubscriber.notifyNext (mergeMap.js:69)
at InnerSubscriber._next (InnerSubscriber.js:11)
at InnerSubscriber.next (Subscriber.js:49)
at Notification.observe (Notification.js:20)

Я снова говорю здесь, я получил данные, но я тоже получил эту ошибку (когда я использую .tap()). Другая ошибка - бесконечный цикл (с .map()).

Я заранее благодарю вас, если вы нашли время, чтобы прочитать это, и я буду признателен вам за помощь, потому что в этот момент я в черномдыра.

Моя фотография бесконечного цикла здесь

Ответы [ 3 ]

0 голосов
/ 06 октября 2019

Я считаю, что этот эффект является виновником:

@Effect()
init$: Observable<RoleActions> = defer(() => {
  return of(new RequestLoadRoles());
});

удалите его и в конструкторе службы выполните

this.store.dispatch(new RequestLoadRoles());

PS .map () используется для преобразования данныхконвейера (это то, что вам нужно: преобразовать список ролей в действие AllRolesLoaded), а .tap () действует как побочный эффект, поэтому на конвейер фактически не влияет

0 голосов
/ 07 октября 2019

Хорошо, я решил свою проблему. Я поместил объяснение ниже.

Из app.module.ts у меня было это:

imports: [
   ...
   EffectsModule.forRoot([]), 
   StoreModule.forRoot(reducers, { // this is made by cmd : ng g store AppState --root --module app.module.ts
     metaReducers,
     runtimeChecks: {
       strictStateImmutability: true,
       strictActionImmutability: true,
     },
   }),
]

И у меня было это на моем front.module.ts:
NB: я решил сделать 2 модуля(1 спереди, 1 сзади)

imports: [
   ...
   StoreModule.forFeature(roleFeatureKey, rolesReducer),
   EffectsModule.forFeature([PermissionEffects, RolesEffects]),
]

Я нашел много примеров для данных инициализации с помощью nrgx:
- https://dev.to/jonrimmer/where-to-initiate-data-load-in-ngrx-358l
и я сделал то же самое.

Итак, мой новый app.module.ts теперь такой:

import { rolesReducer } from './reducer/role';
...
... 
imports: [
   ...
   EffectsModule.forRoot([RolesEffects]),
   StoreModule.forRoot({ roles: rolesReducer }),
   StoreDevtoolsModule.instrument({
     maxAge: 25,
   }),
]

И в front.module.ts:

imports: [
   ...
   i deleted,
   i deleted,
]

Я до сих пор не знаю, почему он не работал раньше,Я думал, что это может сработать, возможно, это моя ошибка.
В документе сказано:

Для функциональных модулей зарегистрируйте свои эффекты, добавив метод EffectsModule.forFeature () в массив импорта ваших NgModule.

Может быть, мы не можем сделать это, когда мы инициализируем данные
Таким образом, это работает отлично. Надеюсь, у меня больше не будет проблем ^^.

0 голосов
/ 06 октября 2019

Вы должны использовать map вместо tap. tap не возвращает значение, поэтому это означает, что вы отправите результат службы обратно в хранилище, которое вызывает ошибку недействительного действия. С map вы преобразуете ответ службы в действительное действие NgRx.

Код, который вы показываете, действителен, поэтому я предполагаю, что что-то еще вызывает бесконечный цикл.

 switchMap(() => this.roleService.getAllRoles().pipe(
   map(roles => {
     return new AllRolesLoaded({ roles });
   }),
   catchError((error) => of(new ErrorLoadRole(error)))
 )),
...