【问题标题】:@ngrx/effects gives TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable@ngrx/effects 给出 TypeError: You provide 'undefined' where a stream is expected.您可以提供 Observable、Promise、Array 或 Iterable
【发布时间】:2018-02-22 22:42:11
【问题描述】:

我有一个场景,我需要在我的 Angular 应用程序中对两个 @Ngrx 操作进行排序。

第一个操作使用一些数据更新状态,第二个操作使用该数据触发服务器调用。我正在使用 Effect 对这两个动作进行排序,并仅在第一个动作完成后触发第二个动作。

为了确保第二个动作包含状态中最近更新的数据,我将 store 注入到我的效果中,并使用选择器函数来检索数据并将其作为第二个动作的有效负载包含在内。

这行得通。

我的问题

但是,我相信由于 this.actions$ 已经是一个可观察的流,我应该能够简单地转换该流上的每个事件并将其转换为不同的操作。

我想到的粗略大理石图:

我正在使用withLatestFrom 运算符将动作流与来自状态的数据结合起来。

  @Effect()
  lazyEffect1$ = this.actions$.ofType(LazyActions.TEST_ACTION_SEQUENCE)
     .withLatestFrom(this.filterId$, (x, y) => {
       return new LazyActionDone(y.number);
     });

但是这段代码给了我一个错误:

TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.

我已经能够使用稍微不同的构造来实现它,它也包含在下面的代码中。

但我想了解为什么 lazyEffect1 不起作用。我的理解力在哪里让我失望?

守则

NgRx 减速器:

// reducers.ts
export function lazyReducer(
  state: any = {},
  action: LazyAction | LazyActionDone
) {

  switch (action.type) {
    case LazyActions.TEST_ACTION_SEQUENCE: {
      console.info('Action Received in Reducer: ', action.type);
      return {
        ...state,
        number: action.payload
      };
    }

    case LazyActions.TEST_ACTION_SEQUENCE_DONE: {
      console.info('Done Received in Reducer: ', action.type);
      return {
        ...state,
        retrieved: action.payload,
        done: true
      };
    }

    default: {
      return state;
    }
  }
}

NgRx 效果:

// effects.ts
@Injectable()
export class LazyEffects {
  private filterId$: Observable<any>;

  constructor(
    private actions$: Actions,
    private store: Store<any>
  ) {
    this.filterId$ = this.store.select(getLazyState);
  }

  // THIS DOES NOT WORK
  @Effect()
  lazyEffect1$ = this.actions$.ofType(LazyActions.TEST_ACTION_SEQUENCE)
     .withLatestFrom(this.filterId$, (x, y) => {
       // console.info(x, y);
       return new LazyActionDone(y.number);
     });

  // THIS WORKS
  @Effect()
  lazyEffect2$ = this.actions$.ofType(LazyActions.TEST_ACTION_SEQUENCE)
    .switchMap((action) => {
      console.info('Action Received in Effects: ', action.type);
      return of(action).withLatestFrom(this.filterId$, (x, y) => new LazyActionDone(y.number));
    });
}

【问题讨论】:

  • 为什么不订阅您的第一个操作正在执行的数据更改,然后触发该订阅中的第二个操作?
  • 我可以。正如我已经提到的,我已经使用稍微不同的代码让它工作了。但我的问题不是“我如何让它工作?”,而是“为什么原始代码不能工作?我对@ngrx 和 observables 的工作方式缺少什么?”
  • 我怀疑问题出在效果初始化上,尤其是this.filterId$。用@Effect 装饰的属性将被移动到构造函数中,我猜this.filterId$ 被分配在它们自己被分配之后。从 RxJS 的角度来看,您声明的两个效果几乎相同。出于这个原因,我倾向于将效果声明为函数,而不是属性。
  • @cartant 是的,如果 @Effect 装饰器在分配 this.filterId$ 之前在内部将属性移动到构造函数中,则可以解释该行为。谢谢!我没有考虑过这种可能性。只是好奇,你能分享一个将效果声明为函数的例子吗?
  • 不是声明Observable&lt;Action&gt; 属性,而是声明一个返回Observable&lt;Action&gt; 的函数并用@Effect 装饰它。 NgRx 将看到它是一个函数并调用它来获取 observable:github.com/ngrx/platform/blob/v6.0.0-beta.3/modules/effects/src/…

标签: angular observable rxjs5 ngrx


【解决方案1】:

只是拼出解决方案@cartantprovided..我相信解决方案是替换这个属性:

  // THIS DOES NOT WORK
  @Effect()
  lazyEffect1$ = this.actions$.ofType(LazyActions.TEST_ACTION_SEQUENCE)
     .withLatestFrom(this.filterId$, (x, y) => {
       // console.info(x, y);
       return new LazyActionDone(y.number);
     });

使用此功能:

  @Effect()
  lazyEffect1$() { 
     return this.actions$.ofType(LazyActions.TEST_ACTION_SEQUENCE)
     .withLatestFrom(this.filterId$, (x, y) => {
       // console.info(x, y);
       return new LazyActionDone(y.number);
     });
   }

@cartant 他在 cmets 中的回答值得称赞,但我想为遇到此问题但没有注意到答案在 cmets 中的其他人提供一个答案/示例。

【讨论】:

    猜你喜欢
    • 2023-02-04
    • 2021-12-10
    • 2018-09-07
    • 2017-09-18
    • 1970-01-01
    • 2023-03-13
    • 2018-10-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多