Reputation: 70416
I want to create a reactive store for a user object. So let me first describe what I have tried. I defined a simple user class:
export class User {
constructor(public $key: string) {}
}
For simplicity the class just contains a key. I use firebase storage and to query this storage I created a service class:
export class UserService {
constructor(private af: AngularFire, private authService: AuthService) {}
getUser() {
return this.authService.authInfo$.concatMap(a => this.af.database.object('/users/' + a.uid));
}
}
getUser()
first fetches the user id and queries the firebase database for the specific user. However I don't want to use the service directly. Rather I would like to have a global store that has a BehaviorSubject, which contains the latest user object. So lets introduce the store:
export class UserStore {
private _user: BehaviorSubject<User> = new BehaviorSubject(null);
constructor(private userService: UserService) {
this.userService.getUser().subscribe(
userJson => {
this._user.next(new User(userJson.$key, userJson.firstName, userJson.lastName, null, userJson.personalDataSheet));
},
err => console.error
);
}
get user() {
return this._user.asObservable();
}
}
Now I use this store in a component like this:
this.userStore.user.subscribe(
user => console.log(user)
);
And it works! The user is logged. However when I do something like
user: User;
...
this.userStore.user.subscribe(
user => this.user = user
);
subscribe will be called just once with the user being null. Why is that? Does this.user = user
create some kind of another subscription and cancels the first one?
Upvotes: 0
Views: 2962
Reputation: 14087
The problem is that at least two different values are being emitted by your BehaviorSubject:
null
— Initial value you provided when instantiating the subject.User
instance — Value emitted as soon as the Firebase call returns.User
instance — Since everything is "live" with Firebase, your subject could potentially keep emitting different instances for the current user (for example, you'd get a new value if the user logged out).So, depending on the point of execution of your .subscribe()
you might end up getting the null
value.
I would try to use a ReplaySubject instead. A ReplaySubject only starts emitting once a value is explicitly pushed into it. This means you can get rid of that initial null
value and as long as Firebase hasn't returned, the user won't be available (= the subscribe will be "waiting").
More specifically, here's the line I'd change:
private _user: ReplaySubject<User> = new ReplaySubject<User>(1);
Upvotes: 4