问题
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 })
})
})
回答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();
}
回答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(); });
};
回答3:
I found a way to do that
onlineUsers(callback){
return this.status_database_ref.where('state','==','online').onSnapshot((querySnapshot)=>{
callback(querySnapshot.size)
})
}
componentDidMount(){
this.unsubscribe = firebaseService.onlineUsers(this.onUpdateOnlineUsers);
console.log(this.unsubscribe)
}
onUpdateOnlineUsers(count){
this.setState({count})
}
componentWillUnmount(){
this.unsubscribe();
}
来源:https://stackoverflow.com/questions/49876800/firebase-return-onsnapshot-promise