最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Firebase firestore onSnapshot - Get real-time updates after initial data query using promises - Stack Overflow

programmeradmin0浏览0评论

In order to show loading progress, I'm trying to wrap my onSnapshot call in a promise upon initial fetch. Data is loading correctly, but real-time updates are not functioning correctly.

Is there a way implement this type of functionality using the onSnapshot method?

Here's my initial data grab. Real-time updates functioned correctly before implementing the promise wrapper:

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]);

Again, data is being loaded to the DOM, but real-time updates are not functioning correctly.

In order to show loading progress, I'm trying to wrap my onSnapshot call in a promise upon initial fetch. Data is loading correctly, but real-time updates are not functioning correctly.

Is there a way implement this type of functionality using the onSnapshot method?

Here's my initial data grab. Real-time updates functioned correctly before implementing the promise wrapper:

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]);

Again, data is being loaded to the DOM, but real-time updates are not functioning correctly.

Share Improve this question asked Jun 21, 2019 at 6:24 mvkdiegnhoiwmvkdiegnhoiw 1621 gold badge2 silver badges12 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 7

onSnapshot is not really patible with promises. onSnapshot listeners listen indefinitely, until you remove the listener. Promises resolve once and only once when the work is done. It doesn't make sense to bine onSnapshot (which doesn't end until you say) with a promise, which resolves when the work is definitely plete.

If you want do get the contents of a query just once, just get() instead of onSnapshot. This returns a promise when all the data is available. See the documentation for more details.

Here's what I think going on with your code: When your ponent mounts, your promise gets executed once via useEffect and that's where you set state. However, subsequent updates via the onSnapshot listener are not going to change the db reference, and therefore will not trigger useEffect again, and therefore will not execute the promise again, and therefore not set state again.

The only code that will execute when you receive a snapshot update is the callback function within .onSnapshot().

To fix this, I think you could try the following (I'm honestly not sure if it'll work, but worth a try):

  1. Create a variable to track the initial load: let isInitialLoad = true;
  2. Inside your promise.then(), add isInitialLoad = false;
  3. Inside your .onSnapshot(), add if (!isInitialLoad) setHeroesArr(newHeroes); – this way, on initial load setHeroesArr gets executed on the promise but on snapshot updates setHeroesAss gets executed in the .onSnapshot() callback

The downside to this approach is that setHeroesArr will be called immediately upon a snapshot change rather than being wrapped in a promise.

Hope this helps!

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论