为了显示加载进度,我试图在初始获取时将onSnapshot调用包装在一个Promise中。数据正在正确加载,但是实时更新无法正常运行。
是否可以使用onSnapshot方法实现此类功能?
这是我最初的数据记录。实时更新在实现承诺包装之前可以正常运行:
const [heroesArr, setHeroesArr] = useState([]);
const db = firebase.firestore();
const dbError = firebase.firestore.FirestoreError;
useEffect(() => {
const promise = new Promise((resolve, reject) => {
db.collection("characterOptions")
.orderBy("votes", "desc")
.onSnapshot(coll => {
const newHeroes = [];
coll.forEach(doc => {
const {
name,
votes
} = doc.data();
newHeroes.push({
key: doc.id,
name,
votes
});
});
if(dbError) {
reject(dbError.message)
} else {
resolve(newHeroes);
}
});
});
promise
.then(result => {
setHeroesArr(result);
})
.catch(err => {
alert(err);
});
}, [db]);
同样,数据正在加载到DOM,但是实时更新无法正常工作。
答案 0 :(得分:0)
onSnapshot与Promise并不真正兼容。 onSnapshot侦听器将无限期侦听,直到删除侦听器为止。工作完成后,承诺只会解决一次。将onSnapshot(直到您说完才结束)与诺言相结合是没有意义的,诺言可以在工作肯定完成时解决。
如果只想获取一次查询的内容,则只需get()而不是onSnapshot。当所有数据可用时,这将返回一个承诺。有关更多详细信息,请参见documentation。
答案 1 :(得分:0)
这就是我想对您的代码进行的操作:装入组件时,您的promise将通过useEffect执行一次,这就是您设置状态的地方。但是,通过onSnapshot侦听器进行的后续更新不会更改db
引用,因此不会再次触发useEffect,因此不会再次执行promise,因此不会再次设置状态。
收到快照更新时将执行的唯一代码是.onSnapshot()中的回调函数。
要解决此问题,我认为您可以尝试以下操作(坦率地说,我不确定是否可以使用,但值得尝试):
let isInitialLoad = true;
isInitialLoad = false;
if (!isInitialLoad) setHeroesArr(newHeroes);
–这样,在初始加载时setHeroesArr在promise上执行,但在快照更新时setHeroesAss在.onSnapshot()回调中执行此方法的缺点是setHeroesArr将在快照更改后立即调用,而不是包装在promise中。
希望这会有所帮助!