根据前一个动作的结果有条件地发送一个动作

Conditionally dispatch an action depending on result of previous action

我有两个操作:验证更新。两者都可以独立分派,但我也希望如果分派更新操作,它首先分派验证操作,只有在没有验证的情况下才继续。

如果这是同步代码,它将类似于:

if (this.backendService.isValid(dataset)) {
    this.backEndService.update(dataset)
}

两个动作的定义(以及对应的成功和错误动作):

export const validateDataset = createAction('[Datasets API] Validate Dataset', props<{ dataset: DataSetDto }>());
export const validateDatasetSuccess = createAction('[Datasets API] Validate Dataset Success', props<{ validationErrors: SgErrorMessage[]}>());
export const validateDatasetFail = createAction('[Datasets API] Validate Dataset Fail', props<{ error: string }>());

export const updateDataset = createAction('[Datasets API] Update Dataset', props<{ dataset: DataSetDto }>());
export const updateDatasetSuccess = createAction('[Datasets API] Update Dataset Success');
export const updateDatasetFail = createAction('[Datasets API] Update Dataset Fail', props<{ errorEntity: SgErrorEntity }>());

尝试 1

如果我有效果监听 updateDataset 并尝试首先发送 validateDataset 它似乎永远不会在应用 .pipe() 时被使用(并且可能在这里我应该使用选择器像 hasValidationErrors 返回 bool 而不是同时使用 filter 运算符):

updateDataset$ = createEffect(() => {
    return this.actions$.pipe(
        ofType(DatasetApiActions.updateDataset),
        mergeMap(action => of(DatasetPageActions.validateDataset({dataset: action.dataset})).pipe(
            withLatestFrom(this.store.pipe(select(getValidationErrors))),
            filter(([action, validationErrors]) => validationErrors.length > 0),
            mergeMap(([action, validationErrors]) => this.datasetService.editDataSet(action.dataset).pipe(
                map(() => DatasetApiActions.updateDatasetSuccess()),
                catchError(error => of(DatasetApiActions.updateDatasetFail({error})))
            ))
        ))
    );
});

尝试 2

我尝试将其拆分为 saveDataset 操作,该操作应按顺序调用 validateDatasetupdateDataset 操作,并将 updateDataset 过滤为仅运行 如果 validationErrors 状态为空。我使用了 concatMap,因此操作可以按顺序触发,它们可能是,但 success/fail 响应稍后出现,因此无法使用验证数据:

saveDataset$ = createEffect(() => {
    return this.actions$.pipe(
        ofType(DatasetPageActions.saveDataset),
        concatMap(action => concat(
            of(DatasetPageActions.validateDataset({dataset: action.dataset})),
            of(DatasetApiActions.updateDataset({dataset: action.dataset}))
        ))
    );
});

updateDataset$ = createEffect(() => {
    return this.actions$.pipe(
        ofType(DatasetApiActions.updateDataset),
        withLatestFrom(this.store.pipe(select(getValidationErrors))),
        filter(([action, validationErrors]) => validationErrors.length == 0),
        tap(([action, validationErrors]) => console.log("validationErrors: " + validationErrors))
    );
}, {dispatch: false});

也许我考虑的方式不对,应该将验证构建为异步表单验证,但我仍然认为这个问题是有效的。

我遇到的问题是我无法向 validateDatasetSuccess 操作添加效果来触发 updateDataset 操作,因为它可能由用户单独调度(并且不仅通过保存)。有什么办法可以实现这种行为吗?

你可以试试这个:

updateDataset$ = createEffect(() => {
  return this.actions$.pipe(
    ofType(DatasetApiActions.updateDataset),

    // dispatch the `validate` action
    tap(action => this.store.dispatch(DatasetPageActions.validateDataset({dataset: action.dataset}))),

    switchMap(
      action => this.store.pipe(
        select(getValidationErrors),
        
        // we want to skip the current state
        // after all, the store is just a `BehaviorSubject`,
        // so subscribing to it will return the current state
        skip(1),
        filter(validationErrors => /* ... your condition here ... */),
        mapTo(action),
      ),
    ),
    mergeMap(
      action => this.datasetService.editDataSet(action.dataset).pipe(
        map(() => DatasetApiActions.updateDatasetSuccess()),
        catchError(error => of(DatasetApiActions.updateDatasetFail({error})))
      )
    )
  );
});