Writing to Firebase Firestore React - reactjs

so I have a problem right now. I'm entering the users dates into cloud firestore like this:
so this is a user collection, with a document by user's id's and then the dates are entered as a list. But whenever I refresh the page and enter new data, all the previous data disappears.
So I'm wondering how do I enter data so that it goes like collection(userCalendar).doc(USERID).collection(dates) and then it has all the user's data entered as strings rather than an array like I've been doing.
My code for the way it's behaving right now is below. Thank you! :)
export const allEvents = [];
const Calendar = () => {
const [date, setData] = useState([]);
const handleDateClick = async (DateClickArg) => {
if (DateClickArg.jsEvent.altKey) {
const title = prompt("Enter title", DateClickArg.dateStr); // allows user to put a title in
// making object
const event = {
title: title ? title : DateClickArg.dateStr,
start: DateClickArg.date,
allDay: true
}
allEvents.push(event)
const db = fire.firestore();
let currentUserUID = fire.auth().currentUser.uid
const doc = await fire
.firestore()
.collection("userCalendar")
.doc(currentUserUID)
.get()
db.collection("userCalendar")
.doc(currentUserUID)
.set({
activites: allEvents
})
}
}

You can use arrayUnion() to add new items to an array however it'll be difficult for you to query activities of a user.
For example, you cannot fetch a single activity from that array but you'll have to fetch all of them get the required one. Additionally, you cannot update an object in an array directly in Firestore.
Also a document has a max size limit of 1 MB so if a user can have many activities, it'll be best to create sub-collection.
I would recommend restructuring the following way:
users -> { userId } -> activities-> { activityId }
(col) (doc) (col) (doc)
All of user's activities/events are now in a sub-collection "activities" and each activity would be a document instead of an array item. With this you can easily read/update/delete a single activity.
Also checkout: Firestore subcollection vs array

Not sure whether this meets your requirement, but from my understanding you just want to update the activities with the allEvents which contains all the updated activities.
db.collection("userCalendar")
.doc(currentUserUID)
.set({
activites: allEvents
})
should become
db.collection("userCalendar")
.doc(currentUserUID)
.set({
activites: allEvents
}, { merge: true })
Or you can use the update method
db.collection("userCalendar")
.doc(currentUserUID)
.update({
activites: allEvents
})
From the docs
To update some fields of a document without overwriting the entire document, use the update() method:
import { doc, setDoc } from "firebase/firestore";
const cityRef = doc(db, 'cities', 'BJ');
setDoc(cityRef, { capital: true }, { merge: true });

It looks like you're overwriting your collection with every code execution:
db.collection("userCalendar")
.doc(currentUserUID)
.set({
activites: allEvents
})
You should consider to make an array union, so that the values are added to your collection instead of overwriting them:
db.collection("userCalendar")
.doc(currentUserUID)
.update({
activites: firebase.firestore.FieldValue.arrayUnion(
{
allEvents
}),
})
Also some examples from firestore docu:
https://cloud.google.com/firestore/docs/samples/firestore-data-set-array-operations

Related

How can I update a nested field in a firestore database?

I am creating a clothing e-commerce application integrated with woocommerce and Firestore.
I currently am trying to build the 'add to wishlist part, here I am struggling with trying to update the items 'favourite' field in the database.
I present my Firestore"
my Firestore database
I have access to the item on my react native side
I need to be able to iterate through the objects, compare the nested object items ID against the idea of the item I am currently clicking on and change the favorite field to true.
Currently, I have tried to do the following, but to no avail.
const like = (item) => {
// db.collection("users").doc(user).collection("wishlist").doc(random).set({
// id:item.id,
// name:item.name,
// })
db.collection("users")
.doc(user)
.collection("products")
.doc("0")
.get()
.then((data) => {
const info = data.data();
});
};
In order to perform an update to an object that exists in an array-type field, you need to find that particular object first. Unfortunately, there is no way you can query a Firestore collection based on a value that exists in an object that is contained in an array. This kind of filtering cannot be achieved using partial data. To solve this, you have to read the array, find the desired elements that need to be updated, perform the update and then write the document back to Firestore.
I have also written an article called:
How to update an array of objects in Firestore?
so at the end I came up with a solution which I thought I would share here:
const like = (item) => {
const newData = { ...Data };
if (newData !== null) {
let index = newData.Products.findIndex((e) => {
return e.id === item.id;
});
console.log(index);
newData.Products[index].favourite = true;
db.collection("users")
.doc(userID)
.collection("products")
.doc("0")
.set(newData)
.then((data) => setData(data))
.catch((error) => console.log(error));
} else {
alert("An error has occured");
}
};
so this takes the data, saves it, adds the particular change and sets it again

How do I structure a fetch request after an other?

I have a React blog and I am using Firebase as a back-end. I use createUserWithEmailAndPassword method and after authentication users are redirected to blog where they can start adding blogs.
I store their posts in a firestore collection "posts". No problem with that.
I also want a user object to be created after login with the user preferences. Let's say a specific theme each one has chosen.
I create a collection called "users" at firestore and where I will store each ones object {user: "random#hotmail.com, theme: "dark" ,isAdmin: false, etc} using addDoc method.
I want this object to be created once though and not every time a user logs in.
How do the check on that "users" collection if the user already exists?
I get the collection getDocs(userCollectionRef) and then I filter the data let's say by a property to see if there is that object there.
And if not I want to add the document using addDoc method.
this is the request:
useEffect(() => {
const createUserData = async () => {
const data = await getDocs(usersCollectionRef);
const docs = data.docs.map((doc) => ({
...doc.data(),
id: doc.id,
}));
const userData = docs.filter((doc) => doc.user === user.email);
if (userData.length === 0) {
await addDoc(usersCollectionRef, {
user: user.email,
isAdmin: false,
theme: "dark",
});
}
};
if (user) {
createUserData();
}
}, [user, usersCollectionRef]);
It seems like I am trying
to check and add to the collection at the same time and this is why it doesn't work.
Any ideas?
Should I have an in-between step where I store what I'm getting from the getDocs in a state or something and then do the second request?
Can anyone explain please?
I changed the if statement to a return like so and it worked.
return (
userData.length === 0 &&
(await addDoc(usersCollectionRef, {
user: user.email,
wordGoal: wordGoal,
}))
);
I guess I return the second promise after the first now that's why
I guess when the getDocs request happens because its asynchronous the data then are empty then the code proceeds to the if statement which at that point is true (we have no records) and so executes it.

Firestore where filter and add data to the result

i got the following code:
const db = firebase.firestore();
var duettsRef = db.collection("duetts");
export const keyExists = (setKey)=>{
duettsRef.where("key", "==", setKey).where("player2", "==", "").get().then(querySnapshot => {
console.log(querySnapshot);
if (!querySnapshot.empty) {
this.db.collection('duett').set
({
player2: firebase.auth().currentUser.uid,
});
}
})
}
I logged the snapshot, and its working good untill this point.
The "key" is unique, so the snapshot is either empty or finding one document with this specific properties.
Screenshot of my firestore structure.
What is not working, that I want if the snapshot is not empty, to edit the "player2" field in the found document, and set it to the current user id.
So for example, if I search for the key:2jskmd21, it would fill the User ID in the player2 field: Like this.
How do I do this correctly?
For this situation, you will want to use .update() rather than .set(). This is because the update method is designed to update values in a document while set requires a merge: true flag however its primary function is to create a document if it doesn't exist which can be counterproductive.
In this code, I am setting the value as you described and by setting the limit to 1, will retrieve only 1 document.
const db = firebase.firestore();
var duettsRef = db.collection("duetts");
export const keyExists = (setKey)=>{
duettsRef.where("key", "==", setKey)
.where("player2", "==", "").limit(1).get()
.then(querySnapshot => {
console.log(querySnapshot);
if (!querySnapshot.empty) {
querySnapshot.docs[0].ref.update({
player2:firebase.auth().currentUser.uid});
}
})
.catch(e => console.log(e));
}

how to save a document to a different collection by reference in firestore in react

This is a problem with either promises or finding a different way to solve the problem.
I have a user, that makes a document about their capabilities e.g.
price:
Bio:
called (contractorPage)
And i have users that look at these.
How do i make it so the users can save these by reference?
using firestore, can you make it so that you add these documents to a collection, but when he original document is changed these are also changed?
my first method is to save the Id of the contractorPage. Which is the user ID (UID) of the firebase auth of the person that made it.
Then To map through these to get all of the saved "id's" documents.
const [ idList, setIdList ] = useState([
])
const [ contractorList, setContractorList ] = useState([
])
useEffect(()=>{
/// IdList has the list of id's
//me.uid is the firebase auth user ID
firestore.collection("SavedId's").doc(me.uid).collection("id").get()
.then( async (querySnapshot) => {
await querySnapshot.forEach(doc => {
console.log(doc.id, " => ", doc.data());
setIdList([...idList, doc.data()]);
console.log("first",idList)
});
console.log("second",idList)
// contractorList has the list
idList.map((data, index) => (
firestore.collection("contractorPages").doc(data.id).get().then((word)=>{
console.log("hi", word.data())
if (word.exists) {
setContractorList([...contractorList, word.data()])
}
})
)
);
})
},[])
This does not work, because the idList.map() function runs before setIdList([...idList, doc.data()]); has finished.
How do I make sure the idList is set before I try to retrieve data from firestore with what is in it?

Fetching Reference Value from Firebase in React Native

Hi I am currently struggling to retrieve the data of a specific field from a collection.
This is the structure of my data:
And my RN code:
onEventCollectionUpdate = (querySnapshot) => {
const events = [];
querySnapshot.forEach((doc) => {
const { user, title, action, verb, latitude, longitude, photo } = doc.data();
// const data = doc.data();
events.push({
id: doc.id,
title,
user,
verb,
latitude,
longitude,
photo
});
});
this.setState({
events,
isLoading: false,
});
}
which is triggered in:
componentDidMount() {
this.unsubscribe = this.events.onSnapshot(this.onEventCollectionUpdate);
}
which is defined in my constructor:
this.events = firebase.firestore().collection('events')
I can retrieve the data, and when I print user or doc.data(), the results are massive and nearly incomprehensible. The const { user, title... bit works fine, except "user."
User is returned as a referenceValue, but doesn't actually return as if it were a value I can do anything with. It seems to be returning a massive object, which I can't figure out how to deal with.
Since it is a reference, is it returning the full users object? If so, how do I deal with that? If it's not, how do I make use of the user that is returned? Ideally I end up with the data from user, but not sure if I need to make extra calls for that.
Any pointers would be great, thank you!

Resources