My firestore onSnapshot() function is being called twice.
let user = firebase.firestore().collection('users').doc(userID).onSnapshot
({
next: (documentSnapshot: firebase.firestore.DocumentSnapshot) =>
{
this.userArray.push(documentSnapshot as User);
console.log(documentSnapshot);
//here
},
error: (firestoreError: firebase.firestore.FirestoreError) =>
{
console.log(firestoreError);
//here
}
});
I have also tried subscribing like in https://firebase.google.com/docs/firestore/query-data/listen#detach_a_listener by including user() at the //here comment but to no avail.
How can I modify such that the function only executes one time, i.e. push only one user object per time instead of twice.
I don't know if this is related to your question. If one is using
firebase.firestore.FieldValue.serverTimestamp()
to give a document a timestamp, then onSnaphot will fire twice. This seem to be because when you add a new document to your database onSnapshot will fire, but the serverTimestamp has not run yet. After a few milliseconds serverTimestamp will run and update you document => onSnapshot will fire again.
I would like to add a small delay before onSnapshot fires (say 0,5s or so), but I couldn't find the way to do this.
You can also make a server side function for onCreate event, I believe that would solve your problem. Maybe your userArray.push-action would be more suitable to execute in server side.
Update: To learn more about the behavior of serverTimestamp()
and why it triggers the listener twice read this article: The secrets of Firestore’s FieldValue.serverTimestamp() — REVEALED!. Also, the official documentation states:
When you perform a write, your listeners will be notified with the new data before the data is sent to the backend.
In the article there are a couple of suggested solutions, one of which is to use the metadata
property of the snapshot
to find whether the Boolean value of metadata.hasPendingWrites
is true (which tells you that the snapshot you’re looking at hasn’t been written to the server yet) or false.
For example, in your case you can check whether hasPendingWrites
is false and then push the object:
if ( !documentSnapshot.metadata.hasPendingWrites ){
// This code will only execute once the data has been written to the server
this.userArray.push(documentSnapshot as User);
console.log(documentSnapshot);
}
In a more generic example, the code will look like this:
firestore.collection("MyCollection")
.onSnapshot( snapshot => {
if ( snapshot.metadata.hasPendingWrites ){
// Local changes have not yet been written to the backend
} else {
// Changes have been written to the backend
}
});
Another useful approach, found in the documentation is the following:
If you just want to know when your write has completed, you can listen to the completion callback rather than using
hasPendingWrites
. In JavaScript, use the Promise returned from your write operation by attaching a .then() callback.
I hope these resources and the various approaches will help anyone trying to figure out a solution.
REFERENCES:
If you need a one time response, use the .get() method for a promise.
firebase.firestore().collection('users').doc(userID).get().then(snap => {
this.userArray = [...this.userArray, snap.doc);
});
However, I suggest using AngularFire (totally biased since I maintain the library). It makes handling common Angular + Firebase tasks much easier.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With