【问题标题】:NGRX - Multiple actions on single effectNGRX - 单一效果的多个动作
【发布时间】:2017-12-30 18:29:35
【问题描述】:

这就是我的效果..

@Effect() uploadImages$ = this.actions$
  .ofType(ProjectActions.UPLOAD_IMAGES)
  .map(action => action.payload)
  .flatMap(project => this.service.uploadImages(project))
    .switchMap((project) => Observable.of(
      this.imageActions.loadImages(),
      this.projectActions.uploadImagesSuccess(project),
    ));

尝试在 uploadImagesSuccess 触发之前从 API 加载图像列表。

我尝试使用 .concatMap 和 .mergeMap 代替 .switchMap,但这似乎不是。

对 Effects 和 Observables 还很陌生,从我所遇到的情况来看,我没有找到任何答案。

只需要在另一个完成后触发一个。

编辑 1

所以 loadImages() 触发,一旦完成 API 调用,它就会触发 loadImagesSuccess()。我想我的目标是......

@Effect() uploadImages$ = this.actions$
  .ofType(ProjectActions.UPLOAD_IMAGES)
  .map(action => action.payload)
  .flatMap(project => this.service.uploadImages(project))
    .switchMap((project) => Observable.of(
      this.imageActions.loadImages(),
      this.imageActions.loadImagesSuccess(), 
      this.projectActions.uploadImagesSuccess(project),
  ));

在更一般的意义上,我将照片保存到 API 上的项目中。我想在 uploadImagesSuccess() 有效负载到达我的选择器之前刷新我的 ImageState,并且由于 ImageState 没有及时更新而具有未定义的图像路径。

编辑 2

我通过这个解决方案来使用 ForkJoin,https://github.com/ngrx/effects/issues/64

@Effect() uploadImages$ = this.actions$
  .ofType(ProjectActions.UPLOAD_IMAGES)
  .map(action => action.payload)
  .flatMap(project => {
    let projectInfo$ = this.projectService.uploadImages(project);
    let imageInfo$ = this.imageService.loadImages();

    return Observable.forkJoin(projectInfo$, imageInfo$);
  })
  .mergeMap((results: any[]) => {
    let myActions: any[] = [];
    let actionGenerators = [
      (data: any) => {
        return this.projectActions.uploadImagesSuccess(data);
      },
      (data: any) => {
        return this.imageActions.loadImagesSuccess(data);
      }
    ];

    results.forEach((data: any, index: number) => {
      myActions.push(actionGenerators[index](data));
    });

    return Observable.from(myActions);
  });

不确定这是否是解决此问题的最佳方法,但只要我延迟 API 调用以加载图像,它就可以工作。在没有延迟的情况下,在保存新图像之前加载图像。目前我正在 API 端延迟 loadImages()。

如果可能,我想要一种更好的方法来处理将 API 调用发送到 imageService.loadImages() 直到 projectService.uploadImages() 返回。然后在返回所有数据后,触发更新存储的操作。

建议使用 concat 而不是 forkJoin 以等待一个 API 调用完成,然后再继续下一个。但这会破坏 results.Foreach() 因为结果不再在数组中。

【问题讨论】:

  • 你应该返回actions,switchMap里面的函数是否返回actions?
  • @Meir 也许我错了,但我认为他们都这样做.. loadImages 运行一个动作,在 API 调用返回一个 loadImagesSuccess() 动作后,它类似于 uploadImagesSuccess()。我在返回数据时没有遇到问题,而是按照数据返回的顺序。由于 API 需要更长的时间,因此在调用 loadImagesSuccess() 之前会触发 uploadImagesSuccess()。理想情况下,我希望在调用 uploadImagesSuccess 之后调用 loadImagesSuccess,但我无法弄清楚。
  • this.imageActions.loadImages() 是做什么的?监听 loadImages 是否有副作用,它正在点击 api 来获取一组图像?
  • @seescode 是的,监听 loadImages() 有一个副作用,一旦从 API 中返回就会触发 loadImagesSuccess()。

标签: action effects ngrx


【解决方案1】:

使用连接:

@Effect() uploadImages$ = this.actions$
  .ofType(ProjectActions.UPLOAD_IMAGES)
  .map(action => action.payload)
  .flatMap(project => this.service.uploadImages(project))
    .switchMap((project) => Observable.concat(
      this.imageActions.loadImages(),
      this.projectActions.uploadImagesSuccess(project),
    ));

或来自:

 @Effect() uploadImages$ = this.actions$
      .ofType(ProjectActions.UPLOAD_IMAGES)
      .map(action => action.payload)
      .flatMap(project => this.service.uploadImages(project))
        .switchMap((project) => Observable.from([
          this.imageActions.loadImages(),
          this.projectActions.uploadImagesSuccess(project)]
        ));

【讨论】:

  • 这对我来说并没有什么不同。我最初遇到了错误,但只是我忘记了括号。否则,这仍然给我相同的结果。
【解决方案2】:

也许你可以试试这个。而不是使用加载图像操作,只需在上传图像副作用中执行加载图像 api 调用。像这样的:

@Effect() uploadImages$ = this.actions$
  .ofType(ProjectActions.UPLOAD_IMAGES)
  .map(action => action.payload)
  .mergeMap(project => this.service.uploadImages(project)
     .mergeMap(() => {
        return this.otherService.loadImages()
          .map((images) => this.projectActions.uploadImagesSuccess({ project: project, images: images))
      }
  ));

然后你可以通过监听 UPLOAD_IMAGES_SUCCESS 动作让两个 reducer 更新状态:

export default function ProjectReducer (state = initialState, action: Action) {
    switch (action.type) {
        case UPLOAD_IMAGES_SUCCESS:
            return // do your update with action.payload.project
        default:
            return state;
    }
};

export default function ImagesReducer (state = initialState, action: Action) {
    switch (action.type) {
        case UPLOAD_IMAGES_SUCCESS:
            return // do your update with action.payload.images
        default:
            return state;
    }
};

您可以在 ngrx 示例应用程序中看到相同的模式。在两个减速器中 ADD_TO_CART 正在被监听:

https://github.com/btroncone/ngrx-examples/blob/master/shopping-cart/src/app/reducers/products.ts

https://github.com/btroncone/ngrx-examples/blob/master/shopping-cart/src/app/reducers/cart.ts

【讨论】:

  • 这接近我所需要的。但是,由于未调用该操作 this.otherService.loadImagesSuccess(images) 永远不会触发,因此有效负载不会更改状态。有没有办法将loadImages()的结果映射到loadImagesSuccess(),然后触发uploadImagesSuccess(project)。
  • 你可以让 uploadImagesSuccess 完成加载图像成功操作,而不是加载图像成功操作吗?因此 uploadImagesSuccess 将做两件事。
  • 不确定这是否可行。 loadImagesSuccess 返回我所有图像的标准化列表。我需要将数据发送到我的 ImagesReducer。类似于:.map(images => this.imageService.loadImagesSuccess(images))。 uploadImagesSuccess 返回我的项目数据,其中项目图像存储为 id 列表。我通过以下方式将该数据发送到我的 ProjectReducer:.map(project => this.projectActions.uploadImagesSuccess(project))。基本上loadImages需要改变ImageState,uploadImagesSuccess需要改变ProjectState。
  • 我认为我不能让 loadImages() 有效负载中的图像列表从 uploadImagesSuccess 更新 ImageState。两者都旨在改变我商店的不同部分。我可能认为这一切都不正确,因此非常感谢任何帮助。
  • 我已经更新了我的答案来描述如何做到这一点。我希望这会有所帮助!
猜你喜欢
  • 1970-01-01
  • 2019-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 2018-11-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多