Reputation: 1210
My Angular 5 application is based on NgRx, a state management library similar to Redux but based on RxJS.
I often need to get the latest value from the store based on the payload of the current action.
In RxJS terminology, it means that I have my main stream that constantly produce items and for each new item I need to create a side stream based on the item's value, get the latest value from this stream, and combine it with the main stream.
At the moment, I do something like this:
@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}) => {
...
})
)
I know it is probably not the best solution and I am looking for something like this (which is not possible with withLatestFrom
operator):
@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]) => {
...
})
)
So my question is: Is there any RxJS operator which is similar to withLatestFrom
which can take a value produced by the first stream as a parameter?
Upvotes: 8
Views: 5948
Reputation: 31
I may be a little late and you've already solved this, but...
Firstly I cant clearly see what you're trying to achieve here. What is your intented behavior?
only when your TableActionType.MOVE_CURSOR
is called and with the latest value from your store
or
when your TableActionType.MOVE_CURSOR
is called or latest value from your store is updated
If it is only when your TableActionType.MOVE_CURSOR
is called and with the latest value from your store, using just the withLatestFrom
should be good enough
@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]) => {
...
})
)
If its is *when your TableActionType.MOVE_CURSOR
is called or latest value from your store is updated then i would use a mergeMap to combine your Actions Observable and your Latest value from the store
//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}) => {
...
});
But beware, this will cause the effect to run EVERY time the selector is updated.(which should be okay assuming your tableId is a memoized.
Upvotes: 0
Reputation: 716
No, as of rxjs 6 I don't believe there is such a built-in operator. I think your current solution is the most straightforward way to do it, I'm using that pattern as well, and it's important to remember to include the first()
or take(1)
to avoid responding to subsequent emissions of the selector.
Upvotes: -1
Reputation: 738
I finally did it...
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
Upvotes: 7
Reputation: 58400
You can use mergeMap
and map
to combine the action with the table selected from the store:
@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]) => {
...
})
)
And you'd need to use first
- or take(1)
- to ensure that the inner observable selected from the store emits only a single value - the table that's to be combined with the action.
Upvotes: 3