I have a Firestore listener that seems to trigger a random number of times. On the first page load, it might trigger 5 times, refreshing the page and it fires 13 times.
@Injectable({
providedIn: 'root',
})
export class AuthService {
user$: BehaviorSubject<SavedUser | undefined> = new BehaviorSubject<SavedUser | undefined>(undefined);
constructor(private angularFireAuth: AngularFireAuth, private firestore: AngularFirestore, private router: Router) {
this.onAuthStateChanged();
}
onAuthStateChanged() {
let counter = 0;
this.angularFireAuth.authState.subscribe(user => {
if (user) {
this.firestore
.collection('user')
.doc<User>(user.uid)
.valueChanges()
.subscribe(userRecord => {
counter++
console.log(counter); // Testing
user.getIdToken(true);
this.user$.next(userRecord);
});
}
});
}
}
The output of the console log:
auth.service.ts:35 1
auth.service.ts:35 2
auth.service.ts:35 3
auth.service.ts:35 4
auth.service.ts:35 5
auth.service.ts:35 6
auth.service.ts:35 7
auth.service.ts:35 8
auth.service.ts:35 9
auth.service.ts:35 10
auth.service.ts:35 11
All I'm looking to do here is refresh the user's token when the document it's backed by changes.
I know the this.angularFireAuth.onAuthStateChanged(user => {...}
is only triggered once, and no changes are happening to the document.
I've tried unsubscribing from the Firestore subscription via onDestroy
however that made no difference.
As a "fix" I thought I would be able to read the first value and stop processing via
this.firestore
.collection('user')
.doc<User>(user.uid)
.valueChanges()
.pipe(first())
.subscribe(userRecord => {...});
Which did work at first sight, however that stops the .valueChanges()
from triggering when the document is later changed.
Any tips?
Well I've solved my actual problem by setting a timestamp on the object via another firestore listener, then comparing the current and previous to only trigger once.
I'll post this code if anyone comes here doing a similar thing, but I'd still want to find out why my subscriber is going off so many times.
Cloud function:
export class UserUpdateListener {
public listen = functions.firestore
.document('user/{uid}')
.onWrite(async (snapshot) => {
const before: User = snapshot.before.data() as User;
const after: User = snapshot.after.data() as User;
const skipUpdate = before.lastUpdate && after.lastUpdate && !before.lastUpdate.isEqual(after.lastUpdate);
if (skipUpdate) {
functions.logger.info('No changes, skipping timestamp update');
return;
}
await snapshot.after.ref.update({ lastUpdate: admin.firestore.FieldValue.serverTimestamp() });
});
}
I then check the timestamp in my client using:
constructor(private angularFireAuth: AngularFireAuth, private firestore: AngularFirestore, private router: Router) {
this.onAuthStateChanged();
let lastUpdate: Timestamp;
this.user$.subscribe(async (user) => {
if (this.authUser && user && user.lastUpdate) {
if (lastUpdate && !user.lastUpdate.isEqual(lastUpdate)) {
await this.authUser.getIdToken(true);
}
lastUpdate = user.lastUpdate;
}
});
}
Hopefully this will save someone some time.