Why is my image not being set after returning image URL?

I need to set a user’s profile picture when they switch to the profile tab. I did that with the below code

  const downloadProfilePic = () => {
    firebase.storage().ref('users/'+ user.uid + '/profile.jpg').getDownloadURL()
    .then(imgURL => {
      console.log("successfully downloaded profile picture");
      setProfilePic(imgURL);
    }).catch(error => {
      console.log('error img ' + error);
      setProfilePic(defaultProfileImage);
    })
  }

However, there was always a slight delay of the user’s picture being set since I would initialize the state of the profile picture to defaultProfileImage when I declared it. So like below

const [profilePic, setProfilePic] = useState(defaultProfileImage);

I tried to get rid of that delay by having the download method return the imgURL and then calling the method inside the useState.

    firebase.storage().ref('users/'+ user.uid + '/profile.jpg').getDownloadURL()
    .then(imgURL => {
      console.log("successfully downloaded profile picture");
      return imgURL;
    }).catch(error => {
      console.log('error img ' + error);
      return defaultProfileImage;
    })
  }

 const [profilePic, setProfilePic] = useState(downloadProfilePic);

However, it doesn’t show the image. I just get a blank profile picture. What am I doing wrong?

Answer

Determining the download URL requires a call to the server, and that takes a bit of time. That explains the flash you see.

The call to the server is executed asynchronously, to prevent blocking the user from using your app while the call is happening. You can’t return asynchronously loaded data from a regular function call, which is executed synchronously.

You could return a Promise, which encapsulates the asynchronous behavior, but that would not change anything about the delay and thus the flash. The useState` hook is pretty much the right way to deal with data that is asynchronously loaded in a ReactJS app.

Consider using other UI tricks to prevent the flash, for example by hiding the entire user-profile until the download URL is also available.