0

I am using Firebase's Cloud Firestore for a web page I'm working on. I have it currently setup to create a new document in the "Users" collection when a new user is added/joined. The issue is when I try to pull the list of users down to iterate over them, I'm not able to.

I have tried iterating over it wither different kinds of loops. The loops don't seem to run as the length of the object when console logging it is 0.

let temp = [];
db.collection("Users").onSnapshot(res => {
  const changes = res.docChanges();
  changes.forEach(change => {
    if (change.type === "added") {
      temp.push({
        id: change.doc.id,
        email: change.doc.data().email
      });
    }
  });
});
console.log(temp);
console.log(temp.length);

I expected the 2nd console log to be 2 but it outputs 0. The weird thing is when I look at the object from the console log above, it shows it has a length of 2 and shows the current data in it:

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Thomas
  • 73
  • 1
  • 5
  • That's because the array is empty when it is logged (because the array populating is in the callback, thus asynchronous), then, when you open (expand) the array, the value is updated, but not the top-level display – Seblor Aug 13 '19 at 12:36

1 Answers1

1

Data is loaded from Firestore asynchronously. Since this may take some time, your main code will continue to run while the data is loading. Then when the data is loaded, your callback functions is called.

You can easily see this in practice by adding some logging:

console.log("Before starting onSnapshot");
db.collection("Users").onSnapshot(res => {
  console.log("Got data");
});
console.log("After starting onSnapshot");

When you run this code, it logs:

Before starting onSnapshot

After starting onSnapshot

Got data

This is probably not the order you expected, but it completely explains why the log statements don't work as you expected. By the time you log the array length, the data hasn't been loaded from the database yet.

The reason logging the array does seem to work, is that Chrome updates the log output after the data has loaded. If you change it to console.log(JSON.stringify(temp));, you'll see that it logs an empty array.

This means that all code that needs access to the data, must be inside the callback, or be called from there:

let temp = [];
db.collection("Users").onSnapshot(res => {
  const changes = res.docChanges();
  changes.forEach(change => {
    if (change.type === "added") {
      temp.push({
        id: change.doc.id,
        email: change.doc.data().email
      });
    }
  });
  console.log(temp);
  console.log(temp.length);
});

As you can probably imagine, many developers who are new to asynchronous APIs run into this problem. I recommend studying some of the previous questions on this topic, such as:

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807