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

Angular 6+NgRx:Selector触发Effect请求无需Action的问题咨询

问题:Angular NGRX Effect中仅在触发Action时发起请求的正确处理方式

我正在用Angular 6和NGRX框架开发,现在在Effect里通过store selector获取请求参数。首次调用SignalAction.LOAD_SIGNAL后,只要store变化导致getRequestParams selector更新,不用触发这个Action就会自动发起请求。

我希望只有触发Action的时候才让整个Observable流完整执行,之前试过在store selector后加take(1),但这样请求只能生效一次。请问该怎么正确处理?

相关代码如下:

@Injectable() 
export class SignalEffect { 
  constructor( 
    private _signalService: SignalService, 
    private _actions$: Actions, 
    private _store$: Store<IStore>, 
  ) {} 

  @Effect() 
  public fetchSignal(): Observable<IAction<SignalModel[] | HttpErrorResponse>> { 
    return this._actions$ 
      .ofType(SignalAction.LOAD_SIGNAL) 
      .pipe( 
        switchMap((action: IAction<void>) => this._store$.select(getRequestParams)), 
        switchMap((requestParams: RequestParamsModel) => { 
          return this._signalService.getSignal(requestParams) 
            .pipe( 
              map((responseModel: SignalResponseModel | HttpErrorResponse) => { 
                if (responseModel instanceof HttpErrorResponse) { 
                  return ErrorsAction.requestFailure(responseModel); 
                } 
                return SignalAction.loadSignalSuccess(responseModel.results); 
              }), 
            ); 
        }), 
      ); 
  } 
}

解决方案

咱们先搞清楚问题根源:你现在用switchMap把Action流切换到了store.select(getRequestParams)的流,这个selector流是持续监听store变化的——也就是说,只要getRequestParams对应的数据在store里更新了,不管有没有新的LOAD_SIGNAL Action,都会触发后续的请求逻辑,这就违背了你“仅在触发Action时发起请求”的需求。

而你之前加take(1)的问题在于:take(1)会让整个Effect流直接完成(complete),后续再触发LOAD_SIGNAL Action时,这个Effect已经没有订阅了,所以只能生效一次。

正确的做法是用withLatestFrom操作符,它会在每次LOAD_SIGNAL Action触发时,一次性获取当前store中getRequestParams的最新值,而不会持续监听selector的变化。这样就保证了只有Action触发时才会发起请求,同时每次Action都能正常处理。

修改后的代码如下:

@Injectable() 
export class SignalEffect { 
  constructor( 
    private _signalService: SignalService, 
    private _actions$: Actions, 
    private _store$: Store<IStore>, 
  ) {} 

  @Effect() 
  public fetchSignal(): Observable<IAction<SignalModel[] | HttpErrorResponse>> { 
    return this._actions$ 
      .ofType(SignalAction.LOAD_SIGNAL) 
      .pipe( 
        // 用withLatestFrom获取当前store的参数,而不是切换到selector流
        withLatestFrom(this._store$.select(getRequestParams)),
        // 解构出action和requestParams(action如果没用可以忽略)
        switchMap(([action, requestParams]: [IAction<void>, RequestParamsModel]) => { 
          return this._signalService.getSignal(requestParams) 
            .pipe( 
              map((responseModel: SignalResponseModel | HttpErrorResponse) => { 
                if (responseModel instanceof HttpErrorResponse) { 
                  return ErrorsAction.requestFailure(responseModel); 
                } 
                return SignalAction.loadSignalSuccess(responseModel.results); 
              }), 
            ); 
        }), 
      ); 
  } 
}

补充说明:

  • withLatestFrom的作用是:当源Observable(这里是LOAD_SIGNAL Action流)发出值时,它会从传入的Observable(这里是store selector流)中获取最新的一个值,然后把两者组合成数组返回。
  • 这样一来,只有当LOAD_SIGNAL Action被触发时,才会执行后续的请求逻辑,完全符合你的需求。
  • 如果你担心selector还没有初始值的情况,可以在withLatestFrom之后加一个filter操作符,确保requestParams存在后再发起请求,比如:
    withLatestFrom(this._store$.select(getRequestParams)),
    filter(([_, params]) => !!params), // 确保参数存在
    switchMap(...)
    

内容的提问来源于stack exchange,提问作者codeBelt

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 10:16:55