I have this huge object that I want to be able to set with a one simple function like below
private setPerson = (partialPerson: Partial<Person>) => {
this.person$.next({ ...this.person$.value, ...partialPerson });
};
But when some of the properties changes I want to be able to run a side-effect like this
personNameEffect$ = this.person$.pipe(
map(({ name }) => name),
distinct(),
tap((name) => {
// Do some complex calculation
this.person$.next({ ...this.person$.value, firstName: name });
})
);
constructor() {
this.personNameEffect$.subscribe()
}
This works like I want it to, but the issue is the execution order of these steps, this is what I think is happening:
setPerson
function is called with a partial object- The side-effect is ran and setting the values (Keeping old value from call before)
- The next inside
setPerson
is ran and overwriting what the side-effect have changed
How can I fix this execution order? I want the next to set the values and then the side-effect to run. Here is a Stackblitz link to a playground where this can be reproduced in.
CodePudding user response:
Your issue is likely related to the nested next
calls and/or convoluted logic flow. Furthermore getting the value out of a BehaviorSubject
using value
is generally frowned upon as its not reactive.
I have modelled your problem in a more reactive flow ie:
Use the BehaviorSubject
only for pushing updates rather than a complete Person
. Therefore no longer using it as a store, and avoiding having to update itself:
personUpdates$ = new BehaviorSubject<Partial<Person>>({});
person$
stream accumulates these updates, and effectively stores the 'current' person:
person$ = this.personUpdates$.pipe(
scan((acc, curr) => ({ ...acc, ...curr }), {
name: '',
firstName: '',
lastName: '',
} as Person)
);
Because of the change to the BehaviorSubject
, the effect no longer needs to access it's value
:
personNameEffect$ = this.person$.pipe(
map(({name}) => name),
distinct(),
tap((name) => {
console.log('Name changed detected');
this.personUpdates$.next({ firstName: name });
})
);
Finally setName
and setLastName
are simplified, again due to the the BehaviorSubject
only requiring a partial value.
setName = (name: string) => {
this.personUpdates$.next({ name });
};
setLastName = (lastName: string) => {
this.personUpdates$.next({ lastName });
};