Angular 6+NgRx:Selector触发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_SIGNALAction流)发出值时,它会从传入的Observable(这里是store selector流)中获取最新的一个值,然后把两者组合成数组返回。- 这样一来,只有当
LOAD_SIGNALAction被触发时,才会执行后续的请求逻辑,完全符合你的需求。 - 如果你担心selector还没有初始值的情况,可以在
withLatestFrom之后加一个filter操作符,确保requestParams存在后再发起请求,比如:withLatestFrom(this._store$.select(getRequestParams)), filter(([_, params]) => !!params), // 确保参数存在 switchMap(...)
内容的提问来源于stack exchange,提问作者codeBelt

