Ngrx Effect分发不同Action的最佳实践方案咨询
解决方案
你的核心问题是如何让changeInteractionSuccess动作关联到触发它的原始动作类型,从而执行不同的跳转逻辑。这里有两种可行方案,无需盲目拆分通知后端的Effect:
方案1:在成功动作中携带原始动作类型
这种方案可以保留现有的单一通知后端Effect,只需要修改成功动作的定义,添加一个字段记录原始动作的类型,然后在跳转逻辑中根据这个字段分支处理。
步骤1:修改成功动作定义
// 调整changeInteractionSuccess动作,新增originType字段 const changeInteractionSuccess = createAction( '[Interaction] Change Success', props<{ interaction: Interaction; originType: string }>() );
步骤2:更新通知后端的Effect
在调用后端接口成功后,将原始动作的type传入成功动作:
notifyChangeInBack$ = createEffect( () => { return this.actions$.pipe( ofType( InteractionActions.interactionAccepted, InteractionActions.interactionSelected, ), exhaustMap(action => { const { interaction } = action; return this.interactionDatasource.notifyInteractionChange(interaction) .pipe( map(() => InteractionActions.changeInteractionSuccess({ interaction, originType: action.type // 携带原始动作类型 })), catchError(() => of(InteractionActions.changeInteractionError({ interaction }))) ) }) ); } );
步骤3:合并跳转逻辑为单一Effect
监听changeInteractionSuccess动作,根据originType执行不同的跳转逻辑:
handleSuccessNavigation$ = createEffect( () => { return this.actions$.pipe( ofType(InteractionActions.changeInteractionSuccess), // 仅当原始动作为interactionSelected时,获取UI状态 concatLatestFrom((action) => action.originType === InteractionActions.interactionSelected.type ? this.interactionFacade.selectInteractionsUi$ : of(null) ), tap(([action, interactionsUi]) => { if (action.originType === InteractionActions.interactionAccepted.type) { // 执行interactionAccepted对应的跳转逻辑 } else if (action.originType === InteractionActions.interactionSelected.type) { // 执行interactionSelected对应的跳转逻辑,使用interactionsUi数据 } }) ); }, { dispatch: false } );
方案2:拆分通知后端Effect,使用不同的成功动作
如果追求动作职责的单一性,可以为每个原始动作创建独立的通知Effect,并定义对应的成功动作,然后分别监听这些成功动作执行跳转。
步骤1:定义独立的成功动作
const interactionAcceptedSuccess = createAction( '[Interaction] Accepted Success', props<{ interaction: Interaction }>() ); const interactionSelectedSuccess = createAction( '[Interaction] Selected Success', props<{ interaction: Interaction }>() );
步骤2:拆分通知后端Effect
将原有的notifyChangeInBack$拆分为两个Effect,分别处理不同的原始动作:
// 处理interactionAccepted的通知逻辑 notifyAcceptedChange$ = createEffect( () => { return this.actions$.pipe( ofType(InteractionActions.interactionAccepted), exhaustMap(action => { const { interaction } = action; // 复用同一个后端调用方法 return this.interactionDatasource.notifyInteractionChange(interaction) .pipe( map(() => InteractionActions.interactionAcceptedSuccess({ interaction })), catchError(() => of(InteractionActions.changeInteractionError({ interaction }))) ) }) ); } ); // 处理interactionSelected的通知逻辑 notifySelectedChange$ = createEffect( () => { return this.actions$.pipe( ofType(InteractionActions.interactionSelected), exhaustMap(action => { const { interaction } = action; return this.interactionDatasource.notifyInteractionChange(interaction) .pipe( map(() => InteractionActions.interactionSelectedSuccess({ interaction })), catchError(() => of(InteractionActions.changeInteractionError({ interaction }))) ) }) ); } );
步骤3:保留独立的跳转Effect(监听对应成功动作)
navigateToCustomer$ = createEffect( () => { return this.actions$.pipe( ofType(InteractionActions.interactionAcceptedSuccess), tap((action) => { // interactionAccepted对应的跳转逻辑 }) ); }, { dispatch: false } ); navigateToLastInteractionState$ = createEffect( () => { return this.actions$.pipe( ofType(InteractionActions.interactionSelectedSuccess), concatLatestFrom(() => this.interactionFacade.selectInteractionsUi$), tap(([action, interactionsUi]) => { // interactionSelected对应的跳转逻辑 }) ); }, { dispatch: false } );
方案对比
- 方案1:代码复用率高,无需拆分现有Effect,但需要在成功动作中携带额外的上下文信息,适合逻辑差异不大的场景。
- 方案2:动作职责更清晰,每个动作的意图明确,后期维护更直观,但会产生少量重复代码(可通过抽取公共服务方法复用后端调用逻辑)。
你可以根据团队的代码风格和业务复杂度选择合适的方案,不需要强制拆分通知后端的Effect。
内容的提问来源于stack exchange,提问作者Alejandro
相关产品推荐
相关产品推荐

