【问题标题】:Store dispatch recalls the http get of the effect several timesstore dispatch 多次调用效果的http get
【发布时间】:2019-06-05 04:08:24
【问题描述】:

在调度期间,我的效果被反复调用,直到我的后端响应并加载数据。我需要帮助来了解如何仅使用一个 GET REQUEST 加载数据,然后在数据实际已经存在的情况下从存储中加载。

      this.cases$ = this.store
          .pipe(
            takeWhileAlive(this),
            select(selectImportTaskCasesData),
            tap(
              (cases) => {
                if (cases.length <= 0) {
                  this.store.dispatch(new ImportTaskLoadCasesAction());
                }
              }),
            filter((cases) => {
              return cases.length > 0;
            }),
            tap(() => {
              this.store.dispatch(new ImportTaskLoadCasesLoadedFromStoreAction());
            }),
            shareReplay()
          );

export const selectCasesData = createSelector(
  selectImportTaskCasesState,
  state => state ? state.cases : []
);

export const selectImportTaskCasesData = createSelector(
  selectCasesData,
  cases => {
    return cases.slice(0);
  }
);


 @Effect()
  ImportCasesLoad$: Observable<any> = this.actions$
    .pipe(
      ofType<ImportTaskLoadCasesAction>(ImportCasesActionTypes.ImportTaskLoadCasesAction),
      map((action: ImportTaskLoadCasesAction) => action),
      switchMap((payload) => {
        return this.importCases.get()
          .pipe(
            map(response => {
              return new ImportTaskLoadCasesSuccessAction({ total: response['count'], cases: response['results'] });
            }),
            catchError((error) => {
              this.logger.error(error);
              return of(new ImportTaskLoadCasesLoadErrorAction(error));
            })
          );
      })
    );

【问题讨论】:

    标签: rxjs angular7 store


    【解决方案1】:

    是的,我有一个 reducer 可以像这样处理我的成功操作:

        case ImportCasesActionTypes.ImportTaskLoadCasesSuccessAction:
      return {
        ...state,
        loading: false,
        cases: action.payload.cases,
        total: action.payload.total
      };
    

    它在我的效果中被调用。

    【讨论】:

    • 我用更多细节更新了我之前的答案。我仍在猜测是什么导致了您的这部分问题。 “在调度期间,我的效果会被反复调用,直到我的后端响应并加载数据。”
    【解决方案2】:

    下面的工作吗?这是假设您有一个处理 ImportTaskLoadCasesSuccessAction 的减速器;也许提供一个工作示例会有所帮助,因为对于如何管理状态存在一些猜测。

       this.cases$ = this.store
          .pipe(
            takeWhileAlive(this),
            select(selectImportTaskCasesData),
            tap(
              (cases) => {
                if (cases.length <= 0) {
                  this.store.dispatch(new ImportTaskLoadCasesAction());
                }
              }),
            // personally, I would have the component/obj that is consuming this.cases$ null check the cases$, removed for brevity
            shareReplay()
          );
    
      export const selectCasesData = createSelector(
        selectImportTaskCasesState,
        state => state ? state.cases : []
      );
    
      export const selectImportTaskCasesData = createSelector(
        selectCasesData,
        cases => {
          return cases.slice(0);
        }
      );
    
    
     @Effect()
      ImportCasesLoad$: Observable<any> = this.actions$
        .pipe(
          ofType<ImportTaskLoadCasesAction>(ImportCasesActionTypes.ImportTaskLoadCasesAction),
          mergeMap(() => this.importCases.get()
              .pipe(
                map(response => {
                  return new ImportTaskLoadCasesSuccessAction({
                    total: response['count'],
                    cases: response['results']
                  });
                }),
                // catch error code removed for brevity
              );
          )
        );
    

    如果您只希望调用this.importCases.get() 触发一次,我建议将动作调度移出.pipe(tap(...))。因为每次订阅都会触发。

    相反,将 this.cases$ 设置为始终返回 select(selectImportTaskCasesData), 的结果。从功能上讲,您可能希望它始终返回一个数组。但这取决于您设计的愿望。 敌人的例子......

    this.cases$ = this.store
      .pipe(
        takeWhileAlive(this),
        select(selectImportTaskCasesData),
      );
    

    另外,就像在构造函数中一样,您可以分派this.store.dispatch(new ImportTaskLoadCasesAction());。如果你希望它只在 case$ 为空时被调用,你总是可以将它包装在一个方法中。

    例如

    export class exampleService() {
      ensureCases(): void {
        this.store.pipe(
          select(selectImportTaskCasesData), 
          take(1)
        ).subscribe(_cases => {
          if (_cases && _cases.length < 1 ) {
            this.store.dispatch(new ImportTaskLoadCasesAction());
          }
        }),
      }
    }
    

    【讨论】:

    • 在你的下方查看我的答案,我用我的减速器案例发布了一个示例
    • reducer case 是否被多次调用?有多少东西订阅了this.cases$?每个订阅都将通过 tap 函数运行,这些函数将重复触发操作 (ImportTaskLoadCasesAction),这将重复触发 this.importCases.get()
    • 我需要 this.cases$ 来像这样填充我的剑道下拉菜单:[data]="cases$ | async" 所以如果我订阅我不能分配这个方法:this.cases$ = this.ensureCases() ,我需要下拉菜单的返回值。
    • 我并不是建议你删除cases$ 的分配。我建议从 api 加载数据的调用可能不需要成为 cases$ 分配的一部分。
    • 好的,所以我必须调用此方法,但随后如何将结果分配给我的 Observable 变量 case$?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-28
    • 2017-04-24
    • 2021-06-22
    • 1970-01-01
    • 2018-05-09
    • 2018-09-18
    相关资源
    最近更新 更多