Firebase saying arrayUnion was called with invalid data - javascript

Im getting an error with firebase because im trying to update two values when I press handleSelect. Only issue is that the first updateDoc works fine as I'm trying to add an array into the "user" specific userChats database, but when I try to do the opposite and add the user array to the "chat" database, it fails.
const handleSelect = async(chat) =>{
const docRef = doc(db, 'users', user?.uid)
const docSnap = await getDoc(docRef)
const addRef = doc(db, 'userChats', user?.uid)
await updateDoc(addRef, {
userChats: arrayUnion(chat)
})
const addRecieverRef = doc(db, 'userChats', chat?.uid)
await updateDoc(addRecieverRef, {
userChats: arrayUnion(user)
})
console.log(chat.uid)
const concatUID = user.uid > chat.uid ? user.uid + chat.uid : chat.uid + user.uid;
if(!docSnap.exists() && user.uid!=chat.uid){
await setDoc(doc(db, 'messages', concatUID), {
messages: [],
})
}
else{
dispatch({type: 'CHANGE_USER', payload: chat})
console.log(chat)
}
}
Error
Chats.js:53 Uncaught (in promise) FirebaseError:
Function arrayUnion() called with invalid data.
Unsupported field value: a custom UserImpl object (found in document userChats/lJ4u4PqWynXAPthz3FVgYaQQ0Do1)
I already checked and all the reference values are correct, and both "user" and "chat" are plain objects

Firestore can only store values of the types indicated in its documentation on data types. The UserImpl object that you are trying to store is not of a supported type, which is what the error message indicates.
If the user object comes from Firebase Authentication, you can call toJSON() on it to get a JSON serialization of its data.

Related

How to create a nested collection when creating a user in Firebase / Firestore where users can save bookmarked items

I want to be able to have a nested collection in firebase/firestore where I can save an authenticated users favorites. I was trying to create the collection when the user is created so I can just read/write to it later but I can't figure out how to create the collection. I have something like this:
//This function creates a new user. If the user already exists, no new document will be created
export const createUserDocumentFromAuth = async (
userAuth,
additionalInfo = {}
) => {
if (!userAuth) return;
const userDocRef = doc(db, 'users', userAuth.uid); //database instance, collection, identifier
const bookmarkRef = doc(db, 'users', userAuth.id, 'bookmarks'); //This triggers error
const userSnapshot = await getDoc(userDocRef);
if (!userSnapshot.exists()) {
//If user snapshot doesn't exist - create userDocRef
const { displayName, email } = userAuth;
const createdAt = new Date();
try {
await setDoc(userDocRef, {
displayName,
email,
createdAt,
...additionalInfo,
});
setDoc(bookmarkRef, { //Try to create a bookmarks collection here
favorites: []
})
} catch (error) {
console.log('Error creating user', error.message);
}
}
//if user data exists
return userDocRef;
};
I can create the user just fine but not another collection at the same time. I also tried just creating the collection when a signed-in user clicks on the bookmark button like this but I get a type error in both cases Uncaught (in promise) TypeError: n is undefined every time.
export const addBookmarkForUser = async (userAuth, showId) => {
const bookmarkRef = doc(db, 'users', userAuth.id, 'bookmarks');
try {
await setDoc(bookmarkRef, {
favorites: showId
});
}catch(error){
console.log('error creating bookmark', error.message)
}
};
I'm pretty new to Firebase / Firestore and all I want is to be able to save an item id in an array for an individual user when they click a button. If saving in an array is not ideal or there is any better way to do this, I am open to any suggestions at this point.
I was trying to create the collection when the user is created so I
can just read/write to it later but I can't figure out how to create
the collection.
A (sub)collection is only created when you create the first document in it. There is no way to materialize an empty collection without a document.
And it is normal that you get an error when using the doc() method as follows
const bookmarkRef = doc(db, 'users', userAuth.id, 'bookmarks');
because this method is used to create a DocumentReference and therefore you need to pass a path with an even number of path segments. In you case you pass 3 segments.
You could very well define the CollectionReference for the bookmarks subcollection as follows, using the collection() method and passing the 3 segments
const bookmarkRef = collection(db, 'users', userAuth.id, 'bookmarks');
but, until you add a document in it, it will not exist in the database.
Conclusion: You will automatically create the user's bookmarks subcollection the first time you create a bookmark for the user.
For example:
const bookmarksCollectionRef = collection(db, 'users', userAuth.id, 'bookmarks');
await bookmarksCollectionRef.add({ ... })

React JS: How can I use the ID of a created doc in Firebase and place it in a modal box?

I am making an e-commerce with React JS and use Firebase as a backend emulator. I've successfully created an order which stores the desired values and the doc is successfully stored in Firebase: the doc simulates the order which corresponds to what the client bought. I want to create a modal box which gives the buyer his order ID, however I can't capture that ID and don't know how to do it, but the console log does give me the ID.
Here is the code for creating the doc which goes to Firebase :
const saveOrder = async () => {
const cartFiltered = cart.map(({ id, title, quantity }) => ({ id, title, quantity }));
const orderToSave = {
buyer: buyer,
items: cartFiltered,
total: cartTotal(),
};
console.log(orderToSave);
const db = getFirestore();
const ordersCollection = collection(db, "orders");
const response = await addDoc(ordersCollection, orderToSave);
console.log(response.id);
}
And here is the code for the part of the modal which gives the ID :
<p> ("Your order ID is: {response.id} . Thanks for shopping!") </p>
I get the following error: "response is not defined".
The calls to Firestore all look fine to me at first glance, so more likely you're not making your response variable available to the UI rendering
To do that, you'll want to store the response in the state, for example by setting it up with a useState hook:
const [response, setResponse] = useState({ id: "" });
...
const res = await addDoc(ordersCollection, orderToSave)
setResponse(res);
console.log(res.id);

getDoc from firestore without searching for specific uid

I'm having an issue getting the collection info. Using this code, I could only get the doc with a specific uid.
const snapShot = doc(db, 'Files', 'someUID');
const getSnapShot = await getDoc(snapShot);
console.log(getSnapShot.data());
I used this code to get all of the collection items but threw an error.
const snapShot = collection(db, 'Files');
const getSnapShot = await getDoc(snapShot);
console.log(getSnapShot.forEach(doc => console.log(doc.data()));
Uncaught (in promise) FirebaseError: Expected type 'wc2', but it was: a custom gc2 object
Q: How do I make it work?
To get all documents from a collection or documents that match a query you should use getDocs() instead of getDoc() that is used to fetch a single document as the function name suggests.
const snapShot = collection(db, 'Files');
const getSnapShot = await getDocs(snapShot);
console.log(getSnapShot.forEach(doc => console.log(doc.data()));

Firebase Query and how to read the output

Above is my Js code
I have a database that has information regarding the location of an apartment, I am trying to search for a specific property to see if it exists in the database. The user will be able to key into the search box to perform the search.
"propertiesRef" is used to store the user input.
I tried storing the data into "q" that I received from querying the database. But I have no idea how to read the result.
This is the console log for "q", but I don't quite understand the information that is shown, I want to know which output in the console should I be looking at and how do I access them?
The query() function just creates an instance Query. You need to use getDocs() function to actually fetch data from Firestore.
const search = (property) => {
const propertiesRef = collection(db, "flats-table");
const q = query(propertiesRef, where("name", "==", property))
return getDocs(q).then((qSnap) => {
const data = qSnap.docs.map(d => ({ id: d.id, ...d.data() }))
console.log(data);
return data;
})
// or use async-await
// const qSnap = await getDocs(q);
}
Checkout the documentation for more examples.

How to read/update object instances converted into JSON from Firebase

I am currently stuck at the process of retrieving classes stored as JSON in firebase (I converted each instance of the object into JSON & stored it). My primary objective is to read a piece of data(at meeting which is also a JSON object) within a JSON object (meeting) stored as value in Firestore, using nodejs in Cloud Functions, as I am trying to create a push notification with FCM.
The code I have:
exports.meetingsUpdate = functions.firestore.document('/meetings/{meetingId}')
.onWrite(async (snap,context) => {
var data = snap.after.data()
data = await data.meeting;
const obj = await data.meeting;
const uids = await obj.groupUID;
const requestor = await obj.requesterName;
const payload = {
notification: {
title: 'Meeting request',
body: 'You have a meeting request from ' + requestor
}
}
for (uid of uids) {
// eslint-disable-next-line no-await-in-loop
const doc = await admin.firestore()
.collection('userNotificationTokens')
.doc(uid)
.get()
// eslint-disable-next-line no-await-in-loop
const token = await doc.data().token
// eslint-disable-next-line no-await-in-loop
const response = await admin.messaging().sendToDevice(token, payload)
.then((res) => {
console.log("Message sent successfully!", res.body)
});
}
return null;
})
Here is how my database mapping looks like
I'm relatively new to Firebase & its functionality and have been stuck with errors like
SyntaxError: Unexpected token u in JSON at position 0
TypeError: Cannot read property 'groupUID' of undefined
TypeError: Cannot read property 'token' of undefined
for quite sometime despite trying various approaches & tweaks.
The first meeting value is a JSON object, and the second meeting attribute is also a JSON object. Apologies for the somewhat convoluted storage of data.

Categories

Resources