【问题标题】:Is there an RxJS operator similar to withLatestFrom but with a parameter?是否有类似于 withLatestFrom 但带有参数的 RxJS 运算符?
【发布时间】:2018-08-19 21:18:29
【问题描述】:

我的 Angular 5 应用程序基于 NgRx,这是一个类似于 Redux 但基于 RxJS 的状态管理库。

我经常需要根据当前操作的有效负载从存储中获取最新值。

在 RxJS 术语中,这意味着我的主流不断产生项目,并且对于每个新项目,我需要根据项目的值创建一个侧流,从该流中获取最新值,并将其与主流。

目前,我正在做这样的事情:

@Effect()
public moveCursor$: Observable<Action> = this.actions$.pipe(
  ofType<TableAction.MoveCursor>(TableActionType.MOVE_CURSOR),
  switchMap(action => this.store$.select(selectTableById(action.payload.cursor.tableId)).pipe(
    first(),
    map(table => ({action, table}))
  )),
  map(({action, table}) => {
    ...
  })
)

我知道这可能不是最好的解决方案,我正在寻找这样的东西(withLatestFrom 运算符不可能):

@Effect()
public moveCursor$: Observable<Action> = this.actions$.pipe(
  ofType<TableAction.MoveCursor>(TableActionType.MOVE_CURSOR),
  withLatestFrom(action => this.store$.select(selectTableById(action.payload.cursor.tableId))),
  map(([action, table]) => {
    ...
  })
)

所以我的问题是:是否有任何类似于withLatestFrom 的 RxJS 运算符可以将第一个流产生的值作为参数?

【问题讨论】:

    标签: angular rxjs ngrx


    【解决方案1】:

    我终于做到了……

    doEffect$ = this.actions$.pipe(
        ofType<someAction>(losActionTypes.someAction),
        switchMap/mergeMap/concatMap(    // according to your logic
        action => of(action).pipe(
           withLatestFrom(this.store.pipe(select(leFancySelector)))
        )),
        tap(console.log)    // tap if you don't believe me
    

    【讨论】:

    • 这避免了first()take(1),但总的来说,我认为它并不比 OP 当前的解决方案更具可读性,并且不提供执行要求的单个运算符。
    【解决方案2】:

    您可以使用mergeMapmap 将操作与从商店中选择的表结合起来:

    @Effect()
    public moveCursor$: Observable<Action> = this.actions$.pipe(
      ofType<TableAction.MoveCursor>(TableActionType.MOVE_CURSOR),
      mergeMap(action => this.store$
        .select(selectTableById(action.payload.cursor.tableId))
        .pipe(
          first(),
          map(table => [action, table])
        )
      ),
      map(([action, table]) => {
        ...
      })
    )
    

    您需要使用first - 或take(1) - 来确保从存储中选择的内部可观察对象仅发出一个值 - 将与操作组合的表。

    【讨论】:

    • 谢谢。但是,即使没有派发新的操作,但存储中表的值发生了变化,它不会开始产生新的值吗?
    • 是的,你是对的,它会的。您还需要使用firsttake(1)
    【解决方案3】:

    我可能有点晚了,你已经解决了这个问题,但是......

    首先,我无法清楚地看到您在这里想要实现的目标。你的意图是什么?

    仅当您的 TableActionType.MOVE_CURSOR 被调用并使用您商店中的最新值时

    当您的 TableActionType.MOVE_CURSOR 被调用或您商店的最新值被更新时


    如果 仅当您的 TableActionType.MOVE_CURSOR 被调用并且具有您商店中的最新值时,则仅使用 withLatestFrom 应该就足够了

    @Effect()
    public moveCursor$: Observable<Action> = this.actions$.pipe(
      ofType<TableAction.MoveCursor>(TableActionType.MOVE_CURSOR),
      withLatestFrom(this.store$.select(selectTableById(action.payload.cursor.tableId))),
      map(([action, table]) => {
        ...
      })
    )
    

    如果它是*当您的 TableActionType.MOVE_CURSOR 被调用或您商店的最新值被更新时,那么我将使用 mergeMap 将您的 Actions Observable 和您的 来自商店的最新价值

    //Actions Observable
    this.actions$.pipe(ofType<TableAction.MoveCursor>(TableActionType.MOVE_CURSOR))
    
    //Latest Value from the store
    this.store$.select(selectTableById(action.payload.cursor.tableId))
    
    //Result:
    @Effect()
    public moveCursor$: Observable<Action> = mergeMap([
      this.actions$.pipe(ofType<TableAction.MoveCursor>(TableActionType.MOVE_CURSOR)), 
      this.store$.select(selectTableById(action.payload.cursor.tableId))
    ]).map(({action, table}) => {
      ...
    });
    

    但请注意,这将导致效果在选择器更新时每次运行。(假设您的 tableId 是记忆化的,这应该没问题。

    【讨论】:

      【解决方案4】:

      不,从 rxjs 6 开始,我不相信有这样的内置运算符。我认为您当前的解决方案是最直接的方法,我也在使用该模式,重要的是要记住包含 first()take(1) 以避免响应选择器的后续发射。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-06-20
        • 2011-04-12
        • 2016-03-29
        • 1970-01-01
        • 2011-10-14
        • 2017-06-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多