我是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()
)。
如果您能抽出宝贵的时间阅读我的文章,我先谢谢您,感谢您的帮助,因为此时此刻我正处于黑洞中。
答案 0 :(得分:0)
您应该使用map
而不是tap
。
tap
不返回任何值,因此,这意味着您会将服务的结果分派回存储区,这将导致无效操作错误。使用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()方法来注册效果。
也许当我们初始化数据时我们不能这样做
这样,它完美地工作。我希望我不会有更多问题^^。