Problem with uploading multiple images in Firebase v9 - reactjs

I have a problem with uploading multiple images in Firebase v9! It works but only with 1 image, even though I'm mapping through my array.
Everything works as I expect until the last part where it doesn't perform the uploading task for every image...
What am I doing wrong? Thank you in advance!
Here my code:
const addImagePost = () => {
for(let i = 0; i < event.target.files.length; i ++) {
const newImage = event.target.files[i]
setSelectedFiles((prevState => [...prevState, newImage]))
}
}
const uploadPost = async (e) => {
e.preventDefault()
if(loadingPost) return;
setLoadingPost(true)
const docRef = await addDoc(collection(db, 'posts'), {
userID: user?.userUID,
username: user?.userName,
caption: inputPost,
profileUser: user?.userPhoto,
timestamp: serverTimestamp()
})
const imageRef = ref(storage, `files/${docRef.id}/image`)
// It only displays the last image of the array
selectedFiles?.map(async(file) => (
await uploadBytes(imageRef, file, 'data_url').then(async () => {
const downloadURL = await getDownloadURL(imageRef)
await updateDoc(doc(db, 'posts', docRef.id), {
image: downloadURL
}, {merge: true})
})
))

Related

Trouble updating Doc in Firestore with React

Im trying to edit a document in my firestore db. I get an error i cant figure out.
Uncaught (in promise) FirebaseError: Expected type 'va', but it was: a custom Fh object
Im passing in an Object to the updateDoc function using the spread operator.
const saveEvent = React.useCallback(() => {
console.log(isChecked)
const checked = [];
isChecked.map((item, index) => {
if (item === true) {
checked.push(index + 1)
}
})
console.log(checked)
const newEvent = {
id: tempEvent.id,
title: popupEventTitle,
description: popupEventDescription,
start: popupEventDate[0].toString(),
end: popupEventDate[1].toString(),
allDay: popupEventAllDay,
status: popupEventStatus,
color: selectedColor,
resource: checked
};
if (isEdit) {
// update the event in the list
const index = myEvents.findIndex(x => x.id === tempEvent.id);
const newEventList = [...myEvents];
newEventList.splice(index, 1, newEvent);
console.log(newEventList)
setMyEvents(newEventList);
// ISSUE IS IN THE UpdateEvent function
const UpdateEvent = async () => {
const userRef = collection(database, 'events');
const q = query(userRef, where('id', '==', `${tempEvent.id}`));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(newEvent)
updateDoc(doc, {
...newEvent,
});
})
}
UpdateEvent()
} else {
// add the new event to the list
setMyEvents([...myEvents, newEvent]);
const getEvents = async () => {
try {
const docRef = await addDoc(collection(database, "events"), {
id: tempEvent.id,
title: popupEventTitle,
description: popupEventDescription,
start: new Date(popupEventDate[0]),
end: new Date(popupEventDate[1]),
allDay: popupEventAllDay,
status: popupEventStatus,
color: selectedColor,
resource: checked
});
console.log("Document written with ID: ", docRef.id);
} catch (e) {
console.error("Error adding document: ", e);
}
}
//console.log(newEvent)
getEvents()
}
setSelectedDate(popupEventDate[0]);
setOpen(false);
}, [isEdit, myEvents, popupEventAllDay, popupEventDate, popupEventDescription, popupEventStatus, popupEventTitle, tempEvent, selectedColor, isChecked]);
Im not sure whats wrong, and googling the issue gives me little to work with. I cant find anything about Expected type 'va', but it was: a custom Fh object anywhere. Not even in the documentation..
Any help greatly appreciated.
EDIT:
Ater logging doc.query i noticed a small Va on the top of the document. Also a small "$h" when logging doc Anyone know anything more about that?
Screenshots:
This occurs when you're updating a document with incorrect document reference. You should use ref property to get the document reference to properly update the document on your foreach loop. See snippet below:
const UpdateEvent = async () => {
const userRef = collection(database, 'events');
const q = query(userRef, where('id', '==', `${tempEvent.id}`));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(newEvent)
// Here. You shouldn't use the doc object itself.
// You must use the `reference` property to get the document reference to update to.
updateDoc(doc.ref, {
...newEvent,
});
})
}
UpdateEvent()
For more information, you may checkout this documentation.
// ISSUE IS IN THE UpdateEvent function
const UpdateEvent = async () => {
const userRef = collection(database, 'events');
const q = query(userRef, where('id', '==',${tempEvent.id}));
if you are using 'where' claouse then you will always get one doc
const querySnapshot = await getDocs(q);
const doc = await getDocs(q); try this
querySnapshot.forEach((doc) => {
console.log(newEvent)
updateDoc(doc, {
...newEvent,
});
})
}
UpdateEvent()
Try this
// ISSUE IS IN THE UpdateEvent function
const UpdateEvent = async () => {
const userRef = collection(database, 'events');
const q = query(userRef, where('id', '==', `${tempEvent.id}`));
const doc = await getDocs(q);
//querySnapshot.forEach((doc) => {
console.log(newEvent)
updateDoc(doc, {
...newEvent,
//});
})
}
UpdateEvent()

FirebaseError: Function updateDoc() called with invalid data

I'm writing a function uploadPost() inside that function I add a doc into firebase firestore collection than I choose a image and update doc by getting download url from firebase but I getting error like
FirebaseError: Function updateDoc() called with invalid data. Unsupported field value: a custom Promise object (found in field image in document posts/mcux46HsSK4mxycOIuev)
My function is given below
const uploadPost = async () => {
if (loading) return;
setLoading(true);
const docRef = await addDoc(collection(db, 'posts'), {
username: session.user.username,
caption: captionRef.current.value,
profileImg: session.user.image,
timestamp: serverTimestamp(),
})
const imageRef = ref(storage, `posts/${docRef.id}/image`);
await uploadString(imageRef, selectedFile, 'data_url').then(async (snapshot) => {
const downloadUrl = getDownloadURL(imageRef);
await updateDoc(doc(db, 'posts', docRef.id), {
image: downloadUrl
});
});
setOpen(false);
setLoading(false);
setSelectedFile(null);
}
What is wrong with that please give some suggestions for this.
const uploadPost = async () => {
if (loading) return;
setLoading(true);
const docRef = await addDoc(collection(db,'posts'), {
username: data?.user?.username,
caption: captionRef.current.value,
profileImg: data?.user?.image,
timestamp: serverTimestamp(),
})
const storage = getStorage(app);
const imageRef = ref(storage, `posts/${docRef.id}/image`);
await uploadString(imageRef, selectedFile, "data_url").then(async () => {
const downloadURL = await getDownloadURL(imageRef);
console.log('this is it' , downloadURL);
await updateDoc(doc(db, 'posts', docRef.id) , {
image: downloadURL,
})
}).catch((err) => {console.log(err)})
setOpen(false);
setLoading(false);
setSelectedFile(null);
}

waiting array of urls after uploading images to firebase storage and then stroing it again to firestore

const sendImageToFirebase = (e) => {
const promises = []
const urlsArray = []
// productimage is an array of image files
productImage.forEach((image, i) => {
var storageRef = firebase.storage().ref();
var uploadTask = storageRef.child(`${userDetailsFirebase.uid}/` + Math.random()).put(image);
promises.push(uploadTask.on('state_changed',
(snapshot) => {
},
(error) => {
console.log("error");
},
async () => {
const downloadurl = await uploadTask.snapshot.ref.getDownloadURL()
urlsArray.push(downloadurl)
}
))
})
Promise.all(promises).then(res => {
db.collection("products").doc(idGeneratedforProduct).set(
{
imageURL: urlsArray, //array of image urls
},
).then(e => {
}).catch(error => console.log("Error while sendig items to Firebase"))
})
}
I want to upload a multiple images to firebase storage. Here, sendImagToFirebase is a normal function in reactJs, and productimage is an array of image files. I want to wait for URL for each image files and then store all of them as an array to firestore. I would appreciate your input on how to do it?
You can create a function that receoves the ref and the file and returns the downloadURL. By calling it for each file with a Promise.all you get as result your array of downloadURLs:
const uploadFileAndGetDownloadURL = async (ref, file) => {
const snap = await ref.put(file);
const downloadURL = await snap.ref.getDownloadURL();
return downloadURL;
};
const sendImageToFirebase = async (e) => {
const promises = [];
productImage.forEach((image, i) => {
var storageRef = firebase.storage().ref();
var ref = storageRef.child(`${userDetailsFirebase.uid}/` + Math.random());
promises.push(uploadFileAndGetDownloadURL(ref, image));
});
//Your array with the urls
const urlsArray = await Promise.all(promises);
};

push object to array react native

I need to get specific users who have the serviceClientID field in the firestore.
those that do, I insert it into the array and put it in my state chats (setChat).
but the problem is that only one user is entering my state and I have two users with this field.
why is only 1 entering and not 2?
code below:
firebase.auth().onAuthStateChanged(async ({ uid }) => {
const servicesCollection = await firestore()
.collection('Providers')
.doc(uid)
.collection('ServiceHistory')
.get();
servicesCollection.docs.forEach(async item => {
if (item.exists && item.data().serviceClientID) {
const clientsCollection = await firestore()
.collection('Clients')
.doc(item.data().serviceClientID)
.get();
// if (item.data().serviceClientID === clientsCollection.id) {
const values = {
id: clientsCollection.id,
name: clientsCollection.data().name.last,
uid,
};
const arr = [];
arr.push(values);
// }
console.log('arrayay', arr);
setChats(arr);
}
});
});
Cause every loop you empty an array.
You have to get the {arr} out of the function. then you need to push the data inside.
const firebaseFunc = () => {
let arr = [];
firebase.auth().onAuthStateChanged(async ({ uid }) => {
const servicesCollection = await firestore()
.collection('Providers')
.doc(uid)
.collection('ServiceHistory')
.get();
servicesCollection.docs.forEach(async item => {
if (item.exists && item.data().serviceClientID) {
const clientsCollection = await firestore()
.collection('Clients')
.doc(item.data().serviceClientID)
.get();
arr.push({
id: clientsCollection.id,
name: clientsCollection.data().name.last,
uid,
});
});
});
setChats(arr);
console.log('arrayay', arr);
}

How to make Async Await Function in React Native?

I want to create a function that is about uploading photo to Firebase Storage with react-native-fetch-blob. I'm using Redux and you can find action functions below:
My problem is that uploadImage function is not running like asynchronous. Firebase function is running before uploadImage, so application give me an error.
I think i can't make a asynchronous function. How can i fix it ?
uploadImage() function:
const uploadImage = async (imageSource, whereToUpload) => {
let imageURL = '';
const mime = 'image/jpg';
const { Blob } = RNFetchBlob.polyfill;
const { fs } = RNFetchBlob;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
console.log('URI =>', imageSource.uri);
let imgUri = imageSource.uri;
let uploadBlob = null;
const imageRef = firebase.storage().ref(whereToUpload + '/' + imageSource.fileName);
const uploadUri = Platform.OS === 'ios' ? imgUri.replace('file://', '') : imgUri;
await fs.readFile(uploadUri, 'base64')
.then((data) => Blob.build(data, { type: `${mime};BASE64` }))
.then((blob) => {
uploadBlob = blob;
return imageRef.put(blob, { contentType: mime });
})
.then(() => {
uploadBlob.close();
// eslint-disable-next-line no-return-assign
return imageURL = imageRef.getDownloadURL();
})
.catch((error) => {
console.log(error);
});
return imageURL;
};
and the main action is:
export const addProjectGroup = (
myUser,
groupName,
groupDescription,
groupProfilePic,
) => dispatch => {
const groupProfileFinalPic = async () => {
let finalGroupPicture = { landscape: '' };
if (_.isEmpty(groupProfilePic.src)) {
await uploadImage(groupProfilePic, 'groupPictures').then((imageURL) => {
console.log('İŞLEM TAMAM!');
console.log('SELECTED IMAGE URL =>', imageURL);
finalGroupPicture.landscape = imageURL;
});
} else {
finalGroupPicture.landscape = groupProfilePic.src.landscape;
}
return finalGroupPicture;
};
console.log("final group profile pic =>", groupProfileFinalPic());
// Önce grubu yaratalım..
// eslint-disable-next-line prefer-destructuring
const key = firebase
.database()
.ref()
.child('groups')
.push().key;
firebase
.database()
.ref('/groups/' + key)
.set({
admin: {
email: myUser.email,
name: myUser.name,
uid: myUser.uid,
},
groupName,
groupDescription,
groupProfilePic: groupProfileFinalPic(),
projects: '',
})
.then(() => {
console.log('Groups oluşturuldu.');
})
.catch(e => {
Alert.alert('Hata', 'Beklenmedik bir hata meydana geldi.');
console.log(e.message);
});
dispatch({
type: ADD_PROJECT_GROUP,
});
};
You are not awaiting groupProfileFinalPic(). This should be done before creating the action you want to dispatch.
groupProfileFinalPic().then(groupProfilePic => {
return firebase
.database()
.ref("/groups/" + key)
.set({
admin: {
email: myUser.email,
name: myUser.name,
uid: myUser.uid
},
groupName,
groupDescription,
groupProfilePic,
projects: ""
})
.then(() => {
console.log("Groups oluşturuldu.");
})
.catch(e => {
Alert.alert("Hata", "Beklenmedik bir hata meydana geldi.");
console.log(e.message);
});
});
I have no clue what the last dispatch is for, you might want to do that in one of the callbacks. Your code is to verbose for an SO question, but I hope this helps anyways.
You are using both await and then on the same call. To use await, you can arrange it something like
const uploadImage = async (imageSource, whereToUpload) => {
...
try {
let data = await RNFS.fs.readFile(uploadUri, 'base64')
let uploadBlob = await Blob.build(data, { type: `${mime};BASE64` }))
...etc...
return finalResult
catch (e) {
// handle error
}
}

Resources