angular - Firestore 活动文档快照侦听器中断排序
问题描述
我遇到了一个firestore问题,希望有人可以帮助我。
我有一个活动的文档快照侦听器,它似乎正在破坏排序行为,我不知道为什么。
在我的组件的构造函数中,我初始化了一次文档快照侦听器:
this.listen = this.fs.collection('users').doc('0EwcWqMVp9j0PtNXFDyO').ref.onSnapshot(t => {
console.log(t)
})
每当我单击“排序”按钮时,它都会初始化一个按名字排序的新集合查询。
async onSort() {
if (this.first) {
this.first();
}
this.sort = this.sort === 'asc'? 'desc' : 'asc';
alert(this.sort)
let arr = []
let lastDoc = null
let queryRef = this.fs.firestore.collection('users').orderBy('firstName', this.sort as any).limit(20);
this.users$ = this._users$.asObservable()
// initialize a blank list
this._users$.next([])
// subscribe to user list snapshot changes
const users1: { users: any, last: any } = await new Promise((resolve, reject) => {
this.first = queryRef.onSnapshot((doc) => {
console.log("Current data: ", doc);
const users = doc.docs.map(t => {
return {
id: t.id,
...t.data() as any
}
})
console.log(users)
resolve({
users: users,
last: doc.docs[doc.docs.length -1]
})
});
})
this._users$.next(this._users$.value.concat(users1.users))
}
但是,这并没有按预期工作。排序返回一条记录(应该有 20 条记录,基于限制)。
有趣的是,取消订阅文档快照会修复排序问题。正在返回 20 条记录,并且排序行为正确。
有人可以解释我做错了什么吗?
这是重现该问题的最小 Stackblitz。
[编辑]
越来越接近解决方案和理解......感谢@ZackReam 的解释以及@Robin 和@Zack 的规范答案。
似乎this._firestore.firestore.collection('users').orderBy('firstName', this.sort as any).limit(20).onSnapshot(...)
确实发出了两次 - 一次用于活动文档侦听器(在缓存中有 1 条记录),第二次用于排序集合(不在缓存中,因为switchMap
每次sort
执行时都会取消订阅)。
我们可以在 Zack 发布的功能演示中看到这一点——它闪烁着与文档侦听器相对应的一条记录,然后在片刻之后,列表中填充了排序后的集合。
你的听众越活跃,结果的闪现就越奇怪。这是设计使然吗?对我来说似乎有缺陷......
直观地说,我希望snapshotChanges()
或valueChanges()
从this._firestore.firestore.collection('users').orderBy('firstName', this.sort as any).limit(20)
查询返回 20 个结果(排序后的前 20 个),独立于任何其他文档侦听器。这个查询的第一个快照是文档查询的初始活动文档,这与直觉相反——这应该与排序查询无关。
解决方案
我在一个简化的环境中使用了您的示例数据,我想我知道发生了什么。
这是我的游乐场,只需更新AppModule
以指向您的 Firebase 项目。
发生了什么?
当您打电话时onSort()
,您正在重新订阅queryRef.onSnapshot
. 此回调触发两次:
- 订阅后,它会立即触发 Firestore 本地缓存中可用的一组初始数据(在您的情况下,它有一个记录,我稍后会解释)。
- 获取实际数据后,它会使用您希望的完整数据集再次触发。
在 Playground 中,确保已订阅Document,然后订阅Sorted Collection。在控制台中,您会看到它两次触发:
不幸的是,由于您将此侦听器包装在 Promise中,因此您只能获得第一次触发,因此只能获得缓存中的单个记录。
为什么取消订阅文档快照会修复它?
似乎本地缓存是根据onSnapshot
当前打开的订阅来填充的。因此,在您的情况下,您有一个侦听器来监听单个记录的快照,因此这是您的缓存中唯一的东西。
在 Playground 中,尝试使用各种订阅状态访问Log Cache 。例如,虽然 only
Document Subscribed
为真,但缓存中只有一条记录。当两个订阅都处于活动状态时,您会在缓存中看到大约 20 条记录。如果您取消订阅所有内容并点击Log Cache,则缓存为空。
事实证明,如果在上面的步骤 1 中没有数据可以从缓存中返回,它会完全跳过该步骤。
在操场上,确保Document已取消订阅,然后订阅Sorted Collection。在控制台中,您会看到它只触发一次:
这恰好适用于您的 Promise,因为第一次也是唯一一次触发包含您希望的数据。
如何解决这个问题?
firebase-js-sdk
回购中的这条评论按预期描述了这种行为,以及原因。
与之配对的 Promise.onSnapshot
是破坏数据流的主要因素,因为.onSnapshot
预计会触发不止一次。你可以改用.get
它,它在技术上会起作用。
然而,正如 Robin 指出的那样,专注于利用@angular/fire
功能的更具反应性的 RxJS 方法)将有很长的路要走。
推荐阅读
- html - 当在父级上使用 aria-live 时,如何从屏幕读取中读取一个子元素?
- php - foreach 没有按预期工作。我究竟做错了什么?
- python - 如何使用日志记录模块记录我的进程 ID
- go - 颤振错误解码来自golang grpc服务器的protobuf响应
- javascript - How to create subset of object, rename some keys and then restructure it?
- javascript - 在对象内使用多个属性向 db.json 发出 POST 请求
- angular - 用 Angular 组件替换 IdentityServer4 登录页面
- excel - 尝试使用 Workbook.SendMail 给收件人时一般邮件失败
- react-native - 在搜索栏中输入文本不会过滤掉名称
- django - Django 过期预订