-7

Here I attach the screenshots of my console log results. Both of them are Objects. But they are not the same. What could be the problem? One is showing

(5) [{..},{..},{..},{..},{..}]

Another one just shows [].

let tmp_array = [];
    this.database.ref('/users/').once('value', (snapshot) => {
      snapshot.forEach( (childSnapshot) => {
      var key = (childSnapshot.val() && childSnapshot.key) || 'Anonymous'; 
      var name = (childSnapshot.val() && childSnapshot.val().name) || 'Anonymous';    
      var email = (childSnapshot.val() && childSnapshot.val().email) || 'Anonymous';
      tmp_array.push({ key: key, email: email, name: name });
    });
    this.setState({ data: tmp_array });
    this.getImageList(tmp_array);
    console.log(tmp_array);
 });


let tmp_array2 = [];
    lodash.forEach(tmp_array, (value, key) => {
      this.storage.ref().child(value.key + '.jpg').getDownloadURL().then(function (url) {
        tmp_array2.push({ id: value.key, image: url });
    });
});
this.setState({ image: tmp_array2 });
console.log(tmp_array2);

enter image description here

GunarathneMDD
  • 180
  • 1
  • 2
  • 11

2 Answers2

0

Welcome to programming with web APIs. You have a typical async problem here. I'll explain what's happening in your code, then give you first steps to a solution, point out some further considerations, and finally provide links with more information for you to read up on this incredibly common (but initially equally confusing) topic.

The problem: the download URLs are loaded asynchronously

Your code calls getDownloadURL, which happens asynchronously. This means that your other code continues while the URL is being loaded. Then when the URL is loaded, your callback is called with the URL. This means that any code that needs the URL, must be inside the callback.

This is easiest to see if you simply the code and add some log statements:

console.log("Before starting getDownloadURL");
this.storage.ref().child(value.key + '.jpg').getDownloadURL().then(function (url) {
    console.log("Got download URL");
});
console.log("After starting getDownloadURL");

When you run this code it prints:

Before starting getDownloadURL

After starting getDownloadURL

Got download URL

That is probably not the order you expected, but it completely explains why the array is empty when you print it: the URLs haven't been loaded yet, so they haven't been added to the array yet.

The solution: put all code that needs the download URL inside the callback

To solve this problem all code that needs the download URL from the database must be inside the callback (or be called from there). So a first step would be:

let tmp_array2 = [];
lodash.forEach(tmp_array, (value, key) => {
  this.storage.ref().child(value.key + '.jpg').getDownloadURL().then(function (url) {
    tmp_array2.push({ id: value.key, image: url });
    this.setState({ image: tmp_array2 });
    console.log(tmp_array2);
  });
});

When you run this, you will see that it logs the array as often as there are child nodes/images. Each time it gets a download URL (remember: this happens asynchronously) it adds it to the array, and then tells React that the state has changed (to it can update the UI).

Some additional considerations and problems

There are more pitfalls in my last code snippet still:

  1. Calling setState() repeatedly may result in a UI that is only partially updated, or in flicker. In that case, consider checking if you've gotten all download URLs before calling setState with something like: if (tmp_array2.length === tmp_array.length) this.setState({ image: tmp_array2 });.
  2. The download URL calls happen asynchronously, and may (I think, it depends on the backend implementation of that API) complete in a different order than in which you call them. In other words: the download URL for the second image may be returned before the download URL for the first image. If this is a concern for your application, be sure to either replicate the other information for the image in tmp_array2 (I think you already do this), or consider storing the download URLs in a way that allows you to associate them back with the items in tmp_array.

More info

As I said at the start: this is a very common problem for developers who are new to dealing with asynchronous APIs to run into. So there are probably hundreds more relevant questions/links if you search for them. Here are some of my favorites:

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

A [] is an empty array. The other [{..},{..},{..},{..},{..}] is an array containing 5 objects. Since you didn't specify which temp array is representing which logged array, I cannot help you determine why one array is empty.

gnomes
  • 39
  • 1
  • 5