firestore onSnapshot 执行两次
Posted
技术标签:
【中文标题】firestore onSnapshot 执行两次【英文标题】:firestore onSnapshot executing twice 【发布时间】:2018-10-02 23:21:30 【问题描述】:我的 firestore onSnapshot() 函数被调用了两次。
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
);
我也尝试过像https://firebase.google.com/docs/firestore/query-data/listen#detach_a_listener 一样通过在 //here 评论中包含 user() 来订阅,但无济于事。
如何修改使函数只执行一次,即每次只推送一个用户对象而不是两次。
【问题讨论】:
【参考方案1】:如果您需要一次性响应,请使用 .get() 方法进行承诺。
firebase.firestore().collection('users').doc(userID).get().then(snap =>
this.userArray = [...this.userArray, snap.doc);
);
但是,我建议使用AngularFire(完全有偏见,因为我维护了库)。它使处理常见的 Angular + Firebase 任务变得更加容易。
【讨论】:
这不是违背了使用onSnapshot
的目的吗?据我了解,get()
不会监听变化。【参考方案2】:
我不知道这是否与您的问题有关。如果有人使用
firebase.firestore.FieldValue.serverTimestamp()
给文档一个时间戳,然后 onSnaphot 会触发两次。这似乎是因为当您向数据库添加新文档时,onSnapshot 会触发,但 serverTimestamp 尚未运行。几毫秒后,serverTimestamp 将运行并更新您的文档 => onSnapshot 将再次触发。
我想在 onSnapshot 触发之前添加一个小延迟(比如 0.5 秒左右),但我找不到这样做的方法。
您也可以为 onCreate 事件创建一个服务器端函数,我相信这会解决您的问题。也许您的 userArray.push-action 更适合在服务器端执行。
更新:要详细了解serverTimestamp()
的行为以及它触发两次侦听器的原因,请阅读这篇文章:The secrets of Firestore’s FieldValue.serverTimestamp() — REVEALED!。此外,官方文档指出:
当您执行写入时,您的侦听器将收到新数据的通知在数据发送到后端之前。
在文章中有几个建议的解决方案,其中之一是使用snapshot
的metadata
属性来查找metadata.hasPendingWrites
的布尔值是否为真(它告诉您快照您'正在查看尚未写入服务器)或false。
例如,在您的情况下,您可以检查hasPendingWrites
是否为假,然后推送对象:
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);
在更通用的示例中,代码如下所示:
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
);
在文档中找到的另一种有用的方法如下:
如果你只想知道你的写入何时完成,你可以收听完成回调而不是使用
hasPendingWrites
。在 javascript 中,通过附加 .then() 回调来使用写入操作返回的 Promise。
我希望这些资源和各种方法能够帮助任何试图找出解决方案的人。
参考:
Events for local changes The hasPendingWrites metadata property Snapshot Listen Options【讨论】:
就我而言,当从客户端设置FieldValue.serverTimestamp()
时,第一个通知包含相关字段的空值;第二个通知包含服务器时间戳值。我最终使用云功能来执行相同的逻辑,它为我解决了双重触发问题。
在我的情况下,这是一个new Date()
用法,这很可能。内部触发firebase.firestore.FieldValue.serverTimestamp()
调用
这里也一样。
metadata.fromCache的值变化也可以触发更新以上是关于firestore onSnapshot 执行两次的主要内容,如果未能解决你的问题,请参考以下文章
哪个反应钩子与firestore onsnapshot一起使用?
如何通过 redux-observable/rxjs 使用 Firestore 实时更新(onSnapshot)?
Cloud Firestore 中 get() 和 onSnapshot() 的区别
将firestore onSnapShot与react redux一起使用的正确方法