Firebase Firestore onSnapshot-使用Promise在初始数据查询后获取实时更新

时间:2019-06-21 06:24:05

标签: javascript reactjs firebase google-cloud-firestore

为了显示加载进度,我试图在初始获取时将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,但是实时更新无法正常工作。

2 个答案:

答案 0 :(得分:0)

onSnapshot与Promise并不真正兼容。 onSnapshot侦听器将无限期侦听,直到删除侦听器为止。工作完成后,承诺只会解决一次。将onSnapshot(直到您说完才结束)与诺言相结合是没有意义的,诺言可以在工作肯定完成时解决。

如果只想获取一次查询的内容,则只需get()而不是onSnapshot。当所有数据可用时,这将返回一个承诺。有关更多详细信息,请参见documentation

答案 1 :(得分:0)

这就是我想对您的代码进行的操作:装入组件时,您的promise将通过useEffect执行一次,这就是您设置状态的地方。但是,通过onSnapshot侦听器进行的后续更新不会更改db引用,因此不会再次触发useEffect,因此不会再次执行promise,因此不会再次设置状态。

收到快照更新时将执行的唯一代码是.onSnapshot()中的回调函数。

要解决此问题,我认为您可以尝试以下操作(坦率地说,我不确定是否可以使用,但值得尝试):

  1. 创建一个变量来跟踪初始负载:let isInitialLoad = true;
  2. 在您的promise.then()中,添加isInitialLoad = false;
  3. 在您的.onSnapshot()内,添加if (!isInitialLoad) setHeroesArr(newHeroes); –这样,在初始加载时setHeroesArr在promise上执行,但在快照更新时setHeroesAss在.onSnapshot()回调中执行

此方法的缺点是setHeroesArr将在快照更改后立即调用,而不是包装在promise中。

希望这会有所帮助!