2

I want to make a function that uploads a photo picked from the react-native-image-picker to firestore, and then returns the value of the URL of the uploaded image. The photo uploads succesfully and I can print in console the download URL, the problem is that when I try to return the URL in the function it is undefined. I tried using an async function to await for the image picker response to finish before I return its value, but it still returns {"_U": 0, "_V": 0, "_W": null, "_X": null}.

uploadImg: async () =>{
        await ImagePicker.launchImageLibrary({ mediaType: 'photo' }, response => {
            if (response.error) {
                alert('Something went wrong');
            } else {
                try {
                    const { path, uri } = response;
                    const fileSource = Platform.OS === 'android' ? uri : path;
                    const { fileName } = response;
                    const storageRef = storage().ref(fileName);
                    
                    storageRef.putFile(fileSource).on('state_changed', snapshot => {
                        switch (snapshot.state) {
                            case 'running':
                                setImageURI(null);
                                break;
                            case 'success':
                                snapshot.ref.getDownloadURL().then(downloadURL => {
                                    setImageURI(downloadURL);
                                    //This console log works, and actually prints the correct url
                                    console.log(`Download URL ${downloadURL}`); 
                                });
                                break;
                            default:
                                break;
                        }
                        
                    });
                } catch (error) {
                    console.log(error)
                }
            }
        }); 
        return imageURI;
    }

I'm pretty sure the problem is not with the state, uploading the picture, or the image picker response. It is likely a wrong usage of the asynchronous functions.

2 Answers2

2

This is because you are returning from your function before the firebase call finishes executing. I made some updates to your code so the function can await a firebase response.

uploadToFirebase: ( fileName, filesource )=>new Promise((resolve, reject)=>{
  const storageRef = storage().ref(fileName);
  storageRef.putFile(fileSource).on('state_changed', snapshot => {
    switch (snapshot.state) {
      case 'running':
        setImageURI(null);
        break;
      case 'success':
        snapshot.ref.getDownloadURL().then(downloadURL => {
          setImageURI(downloadURL);
          //This console log works, and actually prints the correct url
          console.log(`Download URL ${downloadURL}`); 
        });
        break;
      default:
        break;
    }
    resolve();
  });
})
uploadImg: async () =>{
  let response = await ImagePicker.launchImageLibraryAsync({ mediaType: 'photo' });
  if (response.error) {
    alert('Something went wrong');
  } else {
    const { path, uri } = response;
    const fileSource = Platform.OS === 'android' ? uri : path;
    const { fileName } = response;
    await uploadToFirebase(fileName, fileSource)
  }
  return imageURI;
}
Sameer Kumar Jain
  • 2,074
  • 1
  • 13
  • 22
  • Thank you very much! I did have to change little things, such as moving the resolve to the default of the switch, and resolving for success as well. Also I was using the react-native-image-picker which didn't support and async launch, so I had to migrate to expo-image-picker. Lastly in uploadImg(), instead of returning the imageURI I had to return a promise resolution. But you definitely set me in the right track to fix my problem, thank you very much! – Andrés Moreno Jan 22 '21 at 19:48
  • Glad it helps. I think `resolve` could be outside of `switch-case` as well because having it in default doesn’t make sense. – Sameer Kumar Jain Jan 23 '21 at 01:27
1

A very basic usage of async/await would be something like this:

const someCuteAsyncFunc = async () => {
  try {
    let promise = new Promise((resolve, reject) => {
       setTimeout(() => resolve("done!"), 1000)
    });

    let result = await promise; // wait until the promise resolves (*)

    alert(result); // "done!"
  } catch (error) {
    console.error(error);
  }
}

Based on this example.

Given your code, the try/catch, if/else, switch, and return at the very end, I think you need to simplify the function first to make sure you return the image from the promise. There's a very good answer on this topic here.

p-syche
  • 575
  • 1
  • 5
  • 17