firebase return onSnapshot promise
Solution 1
A Promise
in JavaScript can resolve (or reject) exactly once. A onSnapshot
on the other hand can give results multiple times. That's why onSnapshot
doesn't return a promise.
In your current code, you're left with a dangling listener to status_database_ref
. Since you don't do anything with the data, it is wasteful to keep listening for it.
Instead of using onSnapshot
, use get
:
onlineUsers(callback){
this.status_database_ref.where('state','==','online').get((querySnapshot)=>{
callback(querySnapshot.size)
})
}
Or in your original approach:
onlineUsers(){
return this.status_database_ref.where('state','==','online').get();
}
Solution 2
I know it's too late but here is my solution using TypeScript & Javascript.
TYPESCRIPT
const _db=firebase.firestore;
const _collectionName="users";
onDocumentChange = (
document: string,
callbackSuccess: (currentData: firebase.firestore.DocumentData, source?: string | 'Local' | 'Server') => void,
callbackError?: (e: Error) => void,
callbackCompletion?: () => void
) => {
this._db.collection(this._collectionName).doc(document).onSnapshot(
{
// Listen for document metadata changes
includeMetadataChanges: true
},
(doc) => {
const source = doc.metadata.hasPendingWrites ? 'Local' : 'Server';
callbackSuccess(doc.data(), source);
},
(error) => callbackError(error),
() => callbackCompletion()
);
};
JAVASCRIPT (ES5)
var _this = this;
onDocumentChange = function (document, callbackSuccess, callbackError, callbackCompletion) {
_this._db.collection(_this._collectionName).doc(document).onSnapshot({
// Listen for document metadata changes
includeMetadataChanges: true
}, function (doc) {
var source = doc.metadata.hasPendingWrites ? 'Local' : 'Server';
callbackSuccess(doc.data(), source);
}, function (error) { return callbackError(error); }, function () { return callbackCompletion(); });
};
Manspof
Updated on June 16, 2022Comments
-
Manspof almost 2 years
I'm using firebase/firestore and I'm looking a way to return promise of snapshot.
onlineUsers(){ // i want to return onSnapshot return this.status_database_ref.where('state','==','online').onSnapshot(); }
in other file I did
componentDidMount(){ // this.unsubscribe = this.ref.where('state','==','online').onSnapshot(this.onCollectionUpdate) firebaseService.onlineUsers().then(e=>{ console.log(e) }) }
I get the errors
Error: Query.onSnapshot failed: Called with invalid arguments.
TypeError: _firebaseService2.default.unsubscribe is not a function
if i do this way
onlineUsers(){ return this.status_database_ref.where('state','==','online').onSnapshot((querySnapshot)=>{ return querySnapshot }) }
I get
TypeError: _firebaseService2.default.onlineUsers(...).then is not a function
in addition, when I do this way
this.unsubscribe = firebaseService.onlineUsers().then((querySnapshot)=>{ console.log(querySnapshot.size) this.setState({count:querySnapshot.size}) })
// other file
onlineUsers(callback) { return this.status_database_ref.where('state', '==', 'online').get() }
it not listen to change into firebase, means if I change in firebase it's not update or change the size..
---- firestore function --- I tried to make firestore function that trigger each time the UserStatus node updated but this take some seconds and it slow for me.
module.exports.onUserStatusChanged = functions.database .ref('/UserStatus/{uid}').onUpdate((change, context) => { // Get the data written to Realtime Database const eventStatus = change.after.val(); // Then use other event data to create a reference to the // corresponding Firestore document. const userStatusFirestoreRef = firestore.doc(`UserStatus/${context.params.uid}`); // It is likely that the Realtime Database change that triggered // this event has already been overwritten by a fast change in // online / offline status, so we'll re-read the current data // and compare the timestamps. return change.after.ref.once("value").then((statusSnapshot) => { return statusSnapshot.val(); }).then((status) => { console.log(status, eventStatus); // If the current timestamp for this data is newer than // the data that triggered this event, we exit this function. if (status.last_changed > eventStatus.last_changed) return status; // Otherwise, we convert the last_changed field to a Date eventStatus.last_changed = new Date(eventStatus.last_changed); // ... and write it to Firestore. //return userStatusFirestoreRef.set(eventStatus); return userStatusFirestoreRef.update(eventStatus); }); });
function to calculate and update count of online users
module.exports.countOnlineUsers = functions.firestore.document('/UserStatus/{uid}').onWrite((change, context) => { const userOnlineCounterRef = firestore.doc('Counters/onlineUsersCounter'); const docRef = firestore.collection('UserStatus').where('state', '==', 'online').get().then(e => { let count = e.size; return userOnlineCounterRef.update({ count }) }) })
-
Manspof about 6 yearsfrank, I edited my post. I did your way and it not listen to change in firestore. if I change manually the state to offline, the size I get, not change and I do see it invoke again the query
-
Frank van Puffelen about 6 yearsNeither the code in your question, nor in your answer, listens for changes. Both
onSnapshot
andget()
will immediately return the current documents matching the query. The code I've given in my answer now will show you which users are currently online. It's unclear to me at this point what your code is trying to accomplish (it starting to feel like a XY problem). -
Manspof about 6 yearsI want to listen to 'status' collection. if user signup to firebase it change his status with uid to' online', when he close app it change to 'offline'. I want to get the current size of online users. if user in app and other user is close app so it should show the real and update count
-
Manspof about 6 yearshey frank, do you any solution?
-
Frank van Puffelen about 6 yearsI honestly still don't understand what the goal is here. Or maybe it's that I can't figure out how to translate it into code. How is what your (or my) query now returns different from what you need?
-
Manspof about 6 yearsyour query works good, BUT when when I'm on the app and someone new is changed to 'online' or 'offline' the counter is not changed. the counter not calculate again, it not listen to changes! in addition, I edited my post and did it with firestore function but it take like 3-5 seconds to update it and it slow for me
-
Patrick over 3 yearsI wish I could provide 2 upvotes: one for the correct solution and one for giving the solution in TypeScript!
-
Abhishek Tomar almost 2 yearsThanks! @Patrick - just accept (Click the tick button) the answer if this is helpful for you.