I have a scenario where I need to sequence two @Ngrx actions in my Angular application.
The first action updates the state with some data, and the second action uses that data to trigger a server call. I am using an Effect to sequence the two actions and trigger the second action only after the first one has completed.
To ensure the second action includes the recently updated data from the state, I inject the store into my effect and use a selector function to retrieve the data and include it as the payload for the second action.
This works.
My Question
However, I believe that since the this.actions$
is already an observable stream, I should be able to simply transform each event on that stream and convert that into a different action.
A rough marble diagram of what I have in mind:
I am using the withLatestFrom
operator to combine the action stream with the data from the state.
@Effect()
lazyEffect1$ = this.actions$.ofType(LazyActions.TEST_ACTION_SEQUENCE)
.withLatestFrom(this.filterId$, (x, y) => {
return new LazyActionDone(y.number);
});
But this code gives me an error:
TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
I have been able to get this to work using a slightly different construct, which is also included in the code below.
But I would like to understand why the lazyEffect1
is not working. Where is my understanding failing me?
The Code
The NgRx Reducer:
// 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;
}
}
}
The NgRx Effects:
// 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));
});
}
Just to spell out the solution @cartant provided ..I believe the solution is to replace this property:
// 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);
});
With this function:
@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 deserves the credit for his answer in the comments, but I want to provide an answer/example for anyone else who comes across this issue and doesn't notice that the answer is in the comments.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.