我的效果分配了无效的动作或无限循环

时间:2019-10-06 09:25:37

标签: angular typescript ngrx roles ngrx-effects

我是ngrx的新手,我希望有人能和kind并帮助我,因为我阅读了很多有关此问题的主题,而我仍然持相同观点。 我正在测试@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 dispatched an invalid action:
-具有修饰符@Effect(ngrx 8.0.1)的版本,版本createEffect()(ngrx 8.3.0)。
-swithMap() mergeMap() exhaustMap()等...我得到了结果。

这里有完整的代码,也许您可​​以了解这里发生的事情。

//  #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())。

如果您能抽出宝贵的时间阅读我的文章,我先谢谢您,感谢您的帮助,因为此时此刻我正处于黑洞中。

My photo of infinite loop here

3 个答案:

答案 0 :(得分:0)

您应该使用map而不是taptap不返回任何值,因此,这意味着您会将服务的结果分派回存储区,这将导致无效操作错误。使用map,您可以将服务响应转换为有效的NgRx操作。

您显示的代码是有效的,因此我想可能是其他原因导致了无限循环。

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

答案 1 :(得分:0)

我相信这是罪魁祸首:

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

删除它,然后在服务的构造函数中删除

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

PS .map()用于转换管道的数据(这是您所需要的:将角色列表转换为AllRolesLoaded操作),. tap()的作用类似于副作用,因此管道实际上不是影响

答案 2 :(得分:0)

好的,我解决了我的问题。我将说明放在下面。

我从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上有这个标签:
注意:我决定制作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,
]

我仍然不知道为什么它以前没有起作用,我以为它可以起作用,也许这是我的错误。
医生说:

  

对于功能模块,通过在NgModule的imports数组中添加EffectsModule.forFeature()方法来注册效果。   

也许当我们初始化数据时我们不能这样做
这样,它完美地工作。我希望我不会有更多问题^^。