Reputation: 458
I have a features collection in Firestore Database like that.
{
id: 1
title: Title1,
subtitle: Subtitle2
image: features/feature-1.png //point folder name and image name in storage
}
But for using this image in React I need to convert this image into real string URL
for that reason Firebase provide something like that
const getImage = async (imageName:string) => {
const starsRef = ref(storage, imageName);
const imageUrl = await getDownloadURL(starsRef);
return imageUrl;
}
So this function take my image (features/feature-1.png) and convert it into something like that https://firebasestorage.googleapis.com/v0/b/example-app.appspot.com/o/features%2Ffeature-1.png?alt=media&token=a999dfce-ee3a-4e02-95a1-6784e5afb9b9
So I need to loop my Firestore and everytime run this function to get real URL.
const featuresFunc = async () => {
const featureRef = collection(db, "Features");
const data = await getDocs(featureRef);
let arr:any = [];
data.forEach(async (doc:any) => {
arr.push({...doc.data(), image: await getImage(doc.data().image)});
})
console.log(arr)
}
But it return []
array.
Here is the whole code at one page;
import { useEffect} from "react";
import {db, storage} from "../../firebase/config"
import { getDocs, collection } from "firebase/firestore";
import {ref, getDownloadURL } from 'firebase/storage'
const Features = () => {
const getImage = async (imageName:string) => {
const starsRef = ref(storage, imageName);
const imageUrl = await getDownloadURL(starsRef);
return imageUrl;
}
const featuresFunc = async () => {
const featureRef = collection(db, "Features");
const data = await getDocs(featureRef);
let arr:any = [];
data.forEach(async (doc:any) => {
arr.push({...doc.data(), image: await getImage(doc.data().image)});
})
console.log(arr);
}
useEffect(() => {
featuresFunc();
},[])
return null;
};
export default Features;
Summary;
I want to get my objects from Firebase Store and convert it new object with real image URLs.
like that:
{title: "title1", image: features/feature-1.png, .....}
{title: "title2", image: features/feature-2.png, .....}
//into
{title: "title1", image: https://firebasestorage.googleapis.com/v0/b/example1, .....}
{title: "title2", image: https://firebasestorage.googleapis.com/v0/b/example2, .....}
Upvotes: 0
Views: 290
Reputation: 599716
This happens because you're using await
in a forEach
loop. In this code:
const featuresFunc = async () => {
const featureRef = collection(db, "Features");
const data = await getDocs(featureRef);
let arr:any = [];
data.forEach(async (doc:any) => {
arr.push({...doc.data(), image: await getImage(doc.data().image)});
})
console.log(arr)
}
The console.log(arr)
runs before all of the image: await getImage(doc.data().image)
have completed.
My preferred way to deal with this is to use Promise.all
to wait for all promises to complete:
const featuresFunc = async () => {
const featureRef = collection(db, "Features");
const data = await getDocs(featureRef);
let arr:any = [];
let promises = data.docs.map((doc) => getImage(doc.data().image); // 👈
let urls = await Promise.all(promises); // 👈
data.forEach(async (doc:any, i) => {
arr.push({...doc.data(), image: urls[i] }); // 👈
})
console.log(arr)
}
There are other approaches to, so read this for more: Using async/await with a forEach loop
Upvotes: 2