Uploading downloadable URLs to Firestore. await on .map throw 'await' has no effect on the type of this expression

Viewed 120

I built a form using React Hook Form.

User can upload name, address, images etc. My problem is uploading images.

On the Form Submit -> Firstly, I want to upload images to Firebase Storage. After that, I want to grab those downloadable URLs array and store them my listing in Firestore.

I can successfully upload to Firebase Storage. The problem is setting imageUrls state with the urls. For example, If I uploaded 2 files, it returns [undefined, undefined]

Since the component is shrinking, I decided to seperate uploadImages function. But I am facing some asynchronous problems.

Here is uploadFile function

import {
  getStorage,
  ref,
  uploadBytesResumable,
  getDownloadURL,
} from 'firebase/storage';

const uploadImages = (file, fileName) => {
  const storage = getStorage();
  const storageRef = ref(storage, `images/${fileName}`);

  const uploadTask = uploadBytesResumable(storageRef, file);

  // Register three observers:
  // 1. 'state_changed' observer, called any time the state changes
  // 2. Error observer, called on failure
  // 3. Completion observer, called on successful completion
  uploadTask.on(
    'state_changed',
    (snapshot) => {
      // Observe state change events such as progress, pause, and resume
      // Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
      const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
      console.log('Upload is ' + progress + '% done');
      switch (snapshot.state) {
        case 'paused':
          console.log('Upload is paused');
          break;
        case 'running':
          console.log('Upload is running');
          break;
      }
    },
    (error) => {
      // Handle unsuccessful uploads
    },
    () => {
      // Handle successful uploads on complete
      // For instance, get the download URL: https://firebasestorage.googleapis.com/...
      getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
        console.log('File available at', downloadURL);
        return downloadURL;
      });
    }
  );
};

export default uploadImages;

Here is onSubmit event handler

  const onSubmit = async (formData) => {
    setLoading(true);
    try {
      if (geolocationEnabled) {
        const response = await fetch(
          `https://maps.googleapis.com/maps/api/geocode/json?address=${formData.location}&key=${process.env.REACT_APP_GEOCODE_API_KEY}`
        );
        const data = await response.json();

        if (data.status === 'ZERO_RESULTS') {
          setValue('location', undefined);
        } else {
          setValue('location', data.results[0]?.formatted_address);
        }

        const lat = data.results[0]?.geometry.location.lat ?? 0;
        const lng = data.results[0]?.geometry.location.lng ?? 0;

        setValue('geolocation', { lat, lng });
      } else {
        setValue('geolocation', {
          lat: formData.latitude,
          lng: formData.longitute,
        });
      }

      // uploading images
      if (formData.imageUrls.length > 0) {
        const imageURLs = Array.from(formData.imageUrls).map((file) =>
          uploadImages(file, `${formData.userRef}-${file.name}`)
        );
        setValue('imageUrls', imageURLs);
      }

      await addDoc(collection(db, 'listings'), formData);
      toast.success('Listing created successfully!');
    } catch (error) {
      console.log(error);
      toast.error(getErrorMessageForToastify(error.code));
    }
    setLoading(false);
  };

When I write

      if (formData.imageUrls.length > 0) {
        const imageURLs = Array.from(formData.imageUrls).map((file) =>
         { 
           const url = await uploadImages(file, `${formData.userRef}-${file.name}`)
           return url
        }
        );
        setValue('imageUrls', imageURLs);
      }

I receive warning 'await' has no effect on the type of this expression. Since it doesn't return promise. How can I fix this problem?

Thank you

1 Answers

I found the solution by returning Promise in uploadImages function.


    const uploadImages = async (file, fileName) => {
      return new Promise((resolve, reject) => {
        const storage = getStorage();
        const storageRef = ref(storage, `images/${fileName}`);
    
        const uploadTask = uploadBytesResumable(storageRef, file);
    
        // Register three observers:
        // 1. 'state_changed' observer, called any time the state changes
        // 2. Error observer, called on failure
        // 3. Completion observer, called on successful completion
        uploadTask.on(
          'state_changed',
          (snapshot) => {
            // Observe state change events such as progress, pause, and resume
            // Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
            const progress =
              (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
            console.log('Upload is ' + progress + '% done');
            switch (snapshot.state) {
              case 'paused':
                console.log('Upload is paused');
                break;
              case 'running':
                console.log('Upload is running');
                break;
            }
          },
          (error) => {
            // Handle unsuccessful uploads
            reject(error);
          },
          () => {
            // Handle successful uploads on complete
            // For instance, get the download URL: https://firebasestorage.googleapis.com/...
            getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
              console.log('File available at', downloadURL);
              resolve(downloadURL);
            });
          }
        );
      });
    };

And here is the modified one


  // uploading images
  if (formData.images.length > 0) {
    setIsFileUploading(true);
    const imgUrls = await Promise.all(
      [...formData.images].map((file) =>
        uploadImages(
          file,
          `${formData.userRef}-${file.name}-${Math.random()}`
        )
      )
    ).catch(() => {
      toast.error("Image couldn't uploaded");
      return;
    });

    if (imgUrls && imgUrls.length > 0) {
      setValue('imageUrls', imgUrls);
    }
    setIsFileUploading(false);
  }

Related