I falling to get the url return by getDownloadURL appended on the ImageUrls state, getDownloadURL takes a second or two to return the url and it seems like the code continues running and doesn't wait for it to return the url
I am trying to upload multiple images and then create an object in firestore that has the images urls and description
const createAlbum = () => {
addDoc(albumCollection, {
name: albumName,
category: category,
images: imageUrls,
description: description,
});
};
const HandleUpload = (files) => {
files.forEach((file) => {
const storageRef = ref(
storage,
`/files/albums/${albumName}/${file.name}`
);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on(
"state_changed",
(snap) => {},
(err) => {},
() => {
getDownloadURL(uploadTask.snapshot.ref).then((url) => {
setImageUrls((prev) => [...prev, url]);
});
}
);
});
createAlbum();
};
CodePudding user response:
Getting the download URL (like uploading the data, and most modern cloud APIs) is an asynchronous operation. While this call is being handled, your main code indeed continues to execute. Then when the download URL is available, your then
callback is invoked with it, so you can use it.
For this reason, any code that needs the download URL needs to be inside the then
callback, be called from there, or be otherwise synchronized.
The simplest fix is to move createAlbum
into the then
callback:
uploadTask.on(
"state_changed",
(snap) => {},
(err) => {},
() => {
getDownloadURL(uploadTask.snapshot.ref).then((url) => {
setImageUrls((prev) => [...prev, url]);
createAlbum();
});
}
);
If you only want to call createAlbum()
once all uploads are completed, you can keep a counter or use Promise.all()
:
const HandleUpload = (files) => {
let promises = files.map((file) => {
const storageRef = ref(
storage,
`/files/albums/${albumName}/${file.name}`
);
return uploadBytesResumable(storageRef, file).then(() => {
return getDownloadURL(uploadTask.snapshot.ref);
});
});
Promise.all(promises).then((urls) => {
setImageUrls(urls)
createAlbum();
})
};
This code uses the fact that the task returned by uploadBytesResumable
is also a Promise
, so we can use then()
to know when it's completed, and then gets the download URL.
Note that if setImageUrls
is a useState
hook, that operation is asynchronous too. I recommend passing the image URLs to createAlbum
explicitly, rather than trying to pass it through the state, so createAlbum(urls)
CodePudding user response:
Also you can use promise to error and callback to execute one time createAlbum()
like this:
uploadTask.on(
"state_changed",
(snap) => {},
(err) => {},
(),
error => {
console.log('upload error: ', error.message)
},
() => {
getDownloadURL(uploadTask.snapshot.ref).then((url) => {
setImageUrls((prev) => [...prev, url]);
createAlbum();
});
}
)