从组件分派操作时未调用 ngrx 效果
ngrx effect not being called when action is dispatched from component
我遇到了一个问题,ngrx 存储没有发送一个动作来达到应该处理它的效果。
这是尝试分派的组件:
signin() {
this.formStatus.submitted = true;
if (this.formStatus.form.valid) {
this.store.dispatch(new StandardSigninAction(this.formStatus.form.value.credentials));
}
}
操作:
export const ActionTypes = {
STANDARD_SIGNIN: type('[Session] Standard Signin'),
LOAD_PERSONAL_INFO: type('[Session] Load Personal Info'),
LOAD_USER_ACCOUNT: type('[Session] Load User Account'),
RELOAD_PERSONAL_INFO: type('[Session] Reload Personal Info'),
CLEAR_USER_ACCOUNT: type('[Session] Clear User Account')
};
export class StandardSigninAction implements Action {
type = ActionTypes.STANDARD_SIGNIN;
constructor(public payload: Credentials) {
}
}
...
export type Actions
= StandardSigninAction
| LoadPersonalInfoAction
| ClearUserAccountAction
| ReloadPersonalInfoAction
| LoadUserAccountAction;
效果:
@Effect()
standardSignin$: Observable<Action> = this.actions$
.ofType(session.ActionTypes.STANDARD_SIGNIN)
.map((action: StandardSigninAction) => action.payload)
.switchMap((credentials: Credentials) =>
this.sessionSigninService.signin(credentials)
.map(sessionToken => {
return new LoadPersonalInfoAction(sessionToken);
})
);
我可以在调试中看到该组件确实调用了调度方法。我还可以确认 StandardSigninAction
确实被实例化了,因为构造函数中的断点被命中了。
但是standardSignin$
效果没有被调用...
什么可能导致未调用效果?
如何调试商店中发生的事情?
有人可以帮忙吗?
P.S。我在我的进口中运行上述效果如下:
EffectsModule.run(SessionEffects),
编辑:这是我的SessionSigninService.signin方法(return是一个Observable)
signin(credentials: Credentials) {
const headers = new Headers({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'});
const options = new RequestOptions({headers: headers});
const body = 'username=' + credentials.username + '&password=' + credentials.password;
return this.http.post(this.urls.AUTHENTICATION.SIGNIN, body, options).map(res => res.headers.get('x-auth-token'));
}
这不会是一个明确的答案,但希望它会有所帮助。
开始之前:
- 确保您使用的是最新版本的
@ngrx
软件包(适用于您正在使用的 Angular 版本)。
- 如果您更新了任何包,请确保重新启动您的开发环境(即重新启动捆绑程序、服务器等)
如果您还没有这样做,您应该看看 the implementation of the Store
- 以便您对可能出现的问题做出一些有根据的猜测。请注意 Store
非常轻。它既是可观察对象(使用状态作为其来源)又是观察者(服从调度程序)。
如果您查看 store.dispatch
,您会发现它是
store.next
, which calls next
on the Dispatcher
.
所以调用:
this.store.dispatch(new StandardSigninAction(this.formStatus.form.value.credentials));
应该只看到调度程序发出的操作。
注入到您的效果中的 Actions
observable 也很轻。它只是一个使用 Dispatcher
作为其来源的可观察对象。
要查看流过效果的动作,您可以替换为:
@Effect()
standardSignin$: Observable<Action> = this.actions$
.ofType(session.ActionTypes.STANDARD_SIGNIN)
有了这个:
@Effect()
standardSignin$: Observable<Action> = this.actions$
.do((action) => console.log(`Received ${action.type}`))
.filter((action) => action.type === session.ActionTypes.STANDARD_SIGNIN)
ofType
不是运算符;这是一种方法,因此要添加基于 do
的日志记录,需要将其替换为 filter
.
在日志记录到位后,如果您正在接收操作,则效果的实现有问题(或者操作类型的 strings/constants 可能不是您认为的那样,有些不匹配).
如果 effect 没有收到发送的动作,最可能的解释是你发送 StandardSigninAction
的 store
与你发送的 store
不一样effect is using - 也就是说,你有一个 DI 问题。
如果是这样,您应该看看与您所说的其他 SessionEffects
有何不同。 (至少你有一些工作,这是开始试验的好地方。)他们是从不同的模块派遣的吗?调度 StandardSigninAction
的模块是功能模块吗?
如果您破解其中一个正在工作的 SessionEffects
以用 StandardSigninAction
替换其调度的操作,会发生什么情况?那么效果如何运行?
请注意,此答案末尾的问题不是我想要回答的问题;这些是您应该问自己并调查的问题。
您的商店的流可能因为未处理的错误而停止,或者 - 可能更令人困惑 - 看起来 'handled' 使用 .catch
的错误实际上终止了流而没有重新发出新的 Observable 来保持事情进展。
例如,这将终止流:
this.actions$
.ofType('FETCH')
.map(a => a.payload)
.switchMap(query => this.apiService.fetch$(query)
.map(result => ({ type: 'SUCCESS', payload: result }))
.catch(err => console.log(`oops: ${err}`))) // <- breaks stream!
但这会让事情保持活力:
this.actions$
.ofType('FETCH')
.map(a => a.payload)
.switchMap(query => this.apiService.fetch$(query)
.map(result => ({ type: 'SUCCESS', payload: result }))
.catch(e => Observable.of({ type: 'FAIL', payload: e}))) // re-emit
这对于任何 rxjs Observable btw 都是正确的,在向多个观察者广播时考虑这一点尤为重要(就像 ngrx store 在内部使用内部 Subject
)。
我使用的是更高版本的ngrx (7.4.0),所以cartant的建议是:
.do((action) => console.log(`Received ${action.type}`))
应该是...
... = this.actions.pipe(
tap((action) => console.log(`Received ${action.type}`)),
...
最后我发现我错过了将我的新效果导出添加到模块,例如:
EffectsModule.forRoot([AuthEffects, ViewEffects]), // was missing the ', ViewEffects'
另一个可能的原因是,如果您使用 ng generate 创建导入 Effects 的模块,请确保将其导入应用程序模块,因为以下命令 'ng generate module myModule' 不会将其添加到应用程序模块.
如果您使用的是版本 8,请确保用 createEffect
包裹每个操作。
示例:
Create$ = createEffect(() => this.actions$.pipe(...))
我遇到了一个问题,ngrx 存储没有发送一个动作来达到应该处理它的效果。
这是尝试分派的组件:
signin() {
this.formStatus.submitted = true;
if (this.formStatus.form.valid) {
this.store.dispatch(new StandardSigninAction(this.formStatus.form.value.credentials));
}
}
操作:
export const ActionTypes = {
STANDARD_SIGNIN: type('[Session] Standard Signin'),
LOAD_PERSONAL_INFO: type('[Session] Load Personal Info'),
LOAD_USER_ACCOUNT: type('[Session] Load User Account'),
RELOAD_PERSONAL_INFO: type('[Session] Reload Personal Info'),
CLEAR_USER_ACCOUNT: type('[Session] Clear User Account')
};
export class StandardSigninAction implements Action {
type = ActionTypes.STANDARD_SIGNIN;
constructor(public payload: Credentials) {
}
}
...
export type Actions
= StandardSigninAction
| LoadPersonalInfoAction
| ClearUserAccountAction
| ReloadPersonalInfoAction
| LoadUserAccountAction;
效果:
@Effect()
standardSignin$: Observable<Action> = this.actions$
.ofType(session.ActionTypes.STANDARD_SIGNIN)
.map((action: StandardSigninAction) => action.payload)
.switchMap((credentials: Credentials) =>
this.sessionSigninService.signin(credentials)
.map(sessionToken => {
return new LoadPersonalInfoAction(sessionToken);
})
);
我可以在调试中看到该组件确实调用了调度方法。我还可以确认 StandardSigninAction
确实被实例化了,因为构造函数中的断点被命中了。
但是standardSignin$
效果没有被调用...
什么可能导致未调用效果?
如何调试商店中发生的事情?
有人可以帮忙吗?
P.S。我在我的进口中运行上述效果如下:
EffectsModule.run(SessionEffects),
编辑:这是我的SessionSigninService.signin方法(return是一个Observable)
signin(credentials: Credentials) {
const headers = new Headers({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'});
const options = new RequestOptions({headers: headers});
const body = 'username=' + credentials.username + '&password=' + credentials.password;
return this.http.post(this.urls.AUTHENTICATION.SIGNIN, body, options).map(res => res.headers.get('x-auth-token'));
}
这不会是一个明确的答案,但希望它会有所帮助。
开始之前:
- 确保您使用的是最新版本的
@ngrx
软件包(适用于您正在使用的 Angular 版本)。 - 如果您更新了任何包,请确保重新启动您的开发环境(即重新启动捆绑程序、服务器等)
如果您还没有这样做,您应该看看 the implementation of the Store
- 以便您对可能出现的问题做出一些有根据的猜测。请注意 Store
非常轻。它既是可观察对象(使用状态作为其来源)又是观察者(服从调度程序)。
如果您查看 store.dispatch
,您会发现它是
store.next
, which calls next
on the Dispatcher
.
所以调用:
this.store.dispatch(new StandardSigninAction(this.formStatus.form.value.credentials));
应该只看到调度程序发出的操作。
注入到您的效果中的 Actions
observable 也很轻。它只是一个使用 Dispatcher
作为其来源的可观察对象。
要查看流过效果的动作,您可以替换为:
@Effect()
standardSignin$: Observable<Action> = this.actions$
.ofType(session.ActionTypes.STANDARD_SIGNIN)
有了这个:
@Effect()
standardSignin$: Observable<Action> = this.actions$
.do((action) => console.log(`Received ${action.type}`))
.filter((action) => action.type === session.ActionTypes.STANDARD_SIGNIN)
ofType
不是运算符;这是一种方法,因此要添加基于 do
的日志记录,需要将其替换为 filter
.
在日志记录到位后,如果您正在接收操作,则效果的实现有问题(或者操作类型的 strings/constants 可能不是您认为的那样,有些不匹配).
如果 effect 没有收到发送的动作,最可能的解释是你发送 StandardSigninAction
的 store
与你发送的 store
不一样effect is using - 也就是说,你有一个 DI 问题。
如果是这样,您应该看看与您所说的其他 SessionEffects
有何不同。 (至少你有一些工作,这是开始试验的好地方。)他们是从不同的模块派遣的吗?调度 StandardSigninAction
的模块是功能模块吗?
如果您破解其中一个正在工作的 SessionEffects
以用 StandardSigninAction
替换其调度的操作,会发生什么情况?那么效果如何运行?
请注意,此答案末尾的问题不是我想要回答的问题;这些是您应该问自己并调查的问题。
您的商店的流可能因为未处理的错误而停止,或者 - 可能更令人困惑 - 看起来 'handled' 使用 .catch
的错误实际上终止了流而没有重新发出新的 Observable 来保持事情进展。
例如,这将终止流:
this.actions$
.ofType('FETCH')
.map(a => a.payload)
.switchMap(query => this.apiService.fetch$(query)
.map(result => ({ type: 'SUCCESS', payload: result }))
.catch(err => console.log(`oops: ${err}`))) // <- breaks stream!
但这会让事情保持活力:
this.actions$
.ofType('FETCH')
.map(a => a.payload)
.switchMap(query => this.apiService.fetch$(query)
.map(result => ({ type: 'SUCCESS', payload: result }))
.catch(e => Observable.of({ type: 'FAIL', payload: e}))) // re-emit
这对于任何 rxjs Observable btw 都是正确的,在向多个观察者广播时考虑这一点尤为重要(就像 ngrx store 在内部使用内部 Subject
)。
我使用的是更高版本的ngrx (7.4.0),所以cartant的建议是:
.do((action) => console.log(`Received ${action.type}`))
应该是...
... = this.actions.pipe(
tap((action) => console.log(`Received ${action.type}`)),
...
最后我发现我错过了将我的新效果导出添加到模块,例如:
EffectsModule.forRoot([AuthEffects, ViewEffects]), // was missing the ', ViewEffects'
另一个可能的原因是,如果您使用 ng generate 创建导入 Effects 的模块,请确保将其导入应用程序模块,因为以下命令 'ng generate module myModule' 不会将其添加到应用程序模块.
如果您使用的是版本 8,请确保用 createEffect
包裹每个操作。
示例:
Create$ = createEffect(() => this.actions$.pipe(...))