問題描述
NgRx 效果無限循環 (NgRx Effects infinite loop)
我知道這個問題在 SO 上已經被問了一百萬次了。但我需要幫助。我確實意識到我必須在這裡遺漏一些重要的東西。但我沒有看到它。
下面的代碼觸發了一個無限循環。REMOVE_PROJECT 僅被調度一次。REMOVE_PROJECT_SUCCESS 被無限觸發。'removed' 被無限記錄。我不知道為什麼。
所有動作都有唯一的類型。dispatch: false
為 REMOVE_PROJECT_SUCCESS 啟用。
操作:
export const REMOVE_PROJECT = createAction(
'[Project] Remove Project',
props<{ id: string }>()
);
export const REMOVE_PROJECT_SUCCESS = createAction(
'[Project] Remove Project Success',
);
效果:
@Effect()
removeProject$ = createEffect(() => this.actions$.pipe(
ofType(ProjectActions.REMOVE_PROJECT),
switchMap(props =>
this.projects.removeProject(props.id).pipe(
map(() => ({ type: '[Project] Remove Project Success'}),
// have also tried
// map(() => ProjectActions.REMOVE_PROJECT_SUCCESS())
)
))
))
@Effect({ dispatch: false })
removeProjectSuccess$ = createEffect(() => this.actions$.pipe(
ofType(ProjectActions.REMOVE_PROJECT_SUCCESS),
tap(() => console.log('removed')),
))
刪除功能:
removeProject(projectId): Observable<void> {
return from(this.db.doc('projects/' + projectId).ref.delete());
}
參考解法
方法 1:
This is because you're using the @Effect()
decorator with createEffect()
. They both do the same thing under the hood, likely triggering the infinite loop. Remove EITHER the @Effect()
annotations (recommended) or createEffect()
(I'd leave this one).
To be more specific, the createEffect()
also takes {dispatch: false}
as the second argument (after your observable pipe). Since you don't include this option for the second one, it just re‑dispatches the actions you filtered with ofType
, thereby firing the same actions over and over in the infinite loop you're experiencing.
Here's what the second effect should look like if you want to not dispatch:
removeProjectSuccess$ = createEffect(() => this.actions$.pipe(
ofType(ProjectActions.REMOVE_PROJECT_SUCCESS),
tap(() => console.log('removed')),
), { dispatch: false })
Under the hood, both the decorator and the function add annotations to the injectable class that the EffectsModule
consumes. In this scenario, it makes an effect that will not dispatch, and a second one that dispatches by default. I hope this makes sense.
(by Friso Hoekstra、wilsonhobbs)