I have a flag isSuccess
in the state to indicate that the entity is successfully set. I want to read the entity's actual value only when isSuccess
true.
To do this I made such expression:
this.store.success$
.pipe(
filter((isSuccess) => isSuccess),
switchMap(() => this.store.count$)
)
.subscribe((count) => console.log(count));
the patch action changes isSuccess
and count
simultaneously
change() {
this.setState((state) => ({ count: state.count + 1, isSuccess: true }));
}
But for some reason the old value is emitted first and then the new one.
It is looking very weird to me, because such behavior is not an issue for the global store.
I checked updated
function source code and see that a ReplaySubject used for the state storage. There is only one .next
in the func with the next code: tap((newState) => this.stateSubject$.next(newState)),
. I tried to do the same but with my own ReplaySubject
and it works as expected. Here is an example..
What makes the old value to emit and how can I only have my actual value be emitted?
The root cause is not in the updater
function. It is in how the ComponentStore.select
function build the observable. Internally it uses shareReplay
operator (source code). And if you add this operator to your second example you will get the same result.
So what exactly happens:
ngOnInit
where you subscribe on isSuccess$
count$
when the template is compiled (in the html template)count$
select and eventually shareReplay
remembers the valuesetState
is executed and sets isSuccess = true
and count += 1
switchMap
and this operator will synchronously subscribe on the count$
count$
is already cached via shareReplay
just when you subscribe on it will give you the old result because RxJs hasn't started recalculating it for the existing subscriptionIf you remove that subscription from html template you will get it work properly. But it not a solution. What you need to do is to make this change with 'isSuccess' to be async
which means the actual change will be delivered after all synchronous code complete (where a recalculating of your count$
is a part of it). You can achieve this by placing .pipe(observeOn(asapScheduler))
It will move the emission of the value to the async queue of the event loop
and the result will be correct
Here is the corrected example