You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 16:37:45