I have a user object which I observe in a membership service.
I want to update my services only if the user object has relevant changes.
To understand if my user object has relevant changes, I compare my local user object with the observed one. And always assign the new object afterwards.
This does not work, however.
export class MemberService {
private subscription?: Subscription;
user?: User;
constructor(public auth: AuthService) {
this.subscription = this.auth.user$.subscribe((user) => {
const updateServices = this.hasUserRelevantChanges(user)
// apparently this line always fires before the functioncall above?!
this.user = user;
if (updateServices) {
this.updateMyServices();
}
});
}
ngOnDestroy() {
this.subscription?.unsubscribe();
}
hasUserRelevantChanges(user: User | undefined): boolean {
return user?.subscription !== this.user?.subscription ||
user?.username !== this.user?.username ||
user?.isBanned !== this.user?.isBanned;
}
updateMyServices(): void {
// Updating my services!!!
}
}
export class AuthService {
public readonly user$: Observable<User| undefined> = this.user.asObservable();
user: BehaviorSubject<User| undefined> = new BehaviorSubject<User| undefined>(undefined);
constructor(private httpHandler: HttpHandlerService) { ... }
handleUser(): void {
this.httpHandler.login().subscribe(
(user: User) => this.user.next(user));
}
updateUserData(newUser: User): void {
this.user.next(Object.assign(this.user.value, newUser));
}
}
How come that my function hasUserRelevantChanges()
always compares the same, new objects? The local this.user
always holds the new values already within this check, eventhough the assignment this.user = user
comes afterwards?
So how can I understand if my new user
object has the relevant values changed in comparison to the old/previous user-object?
Thanks in advance!
CodePudding user response:
Use RxJs
operators to do so. You don't need to store user somewhere in your code but you can pipe
it with pairwise
and filter
. So it would be something like this:
this.auth.user$.pipe(
pairwise(),
filter(users => userChanged(users[0], users[1]))
... // other operators to handle change - tap, map etc
)
It will be much cleaner if you move logic to proper operators.
Edit: Pairwise
returns two values: previous and current, filter
- as the name says filter events and emits next only if passed callback returns true
Edit2: You should implement userChanged
or adjust your hasUserRelevantChanges
which now you would need to pass 2 arguments.
Edit3: Why your code doesn't work? Because Object.assign
doesn't create new object reference but change original object - so in fact, you are checking everytime the same object.
CodePudding user response:
"How come that my function hasUserRelevantChanges()
always compares the same, new objects?"
That's because you're always assigning the new values to the this.user
variable. Ideally you'd need to update it with the new user
only if it's different. In other words, it must be moved inside the if
block.
this.subscription = this.auth.user$.subscribe((user) => {
const updateServices = this.hasUserRelevantChanges(user)
if (updateServices) {
this.user = user;
this.updateMyServices();
}
});
Update: pairwise
filter
In your scenario, you would do better to use the RxJS pairwise
filter
operators to check the condition.
this.subscription = this.auth.user$.pipe(
pairwise(),
filter(([user1, user2]) => (
user1?.subscription !== user2?.subscription ||
user1?.username !== user2?.username ||
user1?.isBanned !== user2?.isBanned;
))
).subscribe((user) =>
this.updateMyServices();
);
If you're actually comparing all the properties of the object with it's previous values, you could replace the entire pairwise
filter
with the distinctUntilChanged
operator.