Infinite loop inserting docs in firebase after query in react - reactjs

I am trying to get how many documents there are in the firestore to get a correlative document of the day and then save a new document with React. For some reason when I execute the function to save the new document that causes that firestore to add the new record on an infinite loop. Probably I am doing something wrong. Can someone point me out with the error?
this is my function that saves the new document.
import { db } from '../utils/init-firebase'
import { collection, query, onSnapshot, Timestamp, addDoc, where } from "firebase/firestore"
export interface ProjectState {
projectId: string,
name: string;
}
const saveProject = async (project: ProjectState) => {
try {
const today = Timestamp.fromDate(new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate()));
const q = query(collection(db, 'projects'), where('created', '>=', today));
let count = 0
const unsubscribe = onSnapshot(q, (querySnapshot) => {
count = querySnapshot.docs.length + 1;
const date = new Date();
const month = twoDigitDate(date.getMonth() + 1);
const day = twoDigitDate(date.getDate());
const year = date.getFullYear().toString().slice(-2)
const projectId = `${month}${day}${year}${count > 0 ? '-' + count : ''}`;
const projectDoc = collection(db, 'projects');
return addDoc(projectDoc, {
projectId,
name: project.name
created: Timestamp.now(),
});
})
} catch (err) {
console.log('Error creating project:', err)
}
}
const twoDigitDate = (date: number) => {
return date < 10 ? `0${date}` : date;
}
export default saveProject;

You have infinite loop because of onSnapshot() is a listener listen all time collection and when new document appear triggers again with all documents in snapshoot + one extra you added. Insted of onSnapshot() use getDocs() function.
import { collection, query, where, getDocs } from "firebase/firestore";
const q = query(collection(db, "cities"), where("capital", "==", true));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
});
Your function should lool like this ?
const saveProject = async (project: ProjectState) => {
try {
const today = Timestamp.fromDate(new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate()));
const q = query(collection(db, 'projects'), where('created', '>=', today));
let count = 0
const querySnapshot = await getDocs(q)
querySnapshot.forEach(doc => {
// your code what ever you want to do.
})
} catch (err) {
console.log('Error creating project:', err)
}
}
I don't have any clue why you wanna make new docs in same collection according to old documents in this collection. You need to remember using only query you using now you will get max 100 documents.

Related

useEffect didnt run

So i have this function that i want to run once when the app start. This function task is to create userId then i will run another function to fetch data from firebase with the userId that created before. But the fetch function didn't start or it didnt do the task well, there is no sign of error, that's what make it more confusing. If i press the fetch function by button it work correctly.
the state
const [task, setTask] = useState(); // bisa di sebut sebagai controller text input
const [taskItems, setTaskItems] = useState([]); // state untuk list task
const [userId, setUserId] = useState();
const [isLoading, setIsLoading] = useState(true);
const baseUrl =
'https://react-http-post-RANDOM_KEY-default-rtdb.firebaseio.com/task/' + userId;
this is function to create userId function on init app
const handleCreateUser = async () => {
setIsLoading(true);
try {
const value = await AsyncStorage.getItem('userId');
if (value !== null) {
setUserId(value);
} else {
const uniqueId = makeid(6);
await AsyncStorage.setItem('userId', 'user' + uniqueId);
setUserId('user' + uniqueId);
}
await fetchDatabase();
} catch (error) {
console.log('errorrr AsyncStorage' + error);
}
setIsLoading(false);
};
this is function to fetch data from firebase
const fetchDatabase = async () => {
console.log('infinite looping');
try {
const response = await fetch(baseUrl + '.json');
if (!response.ok) {
throw new Error('Something went wrong!');
}
const data = await response.json();
// looping Map/Object dengan key sebagai indexnya
const loadedTask = [];
for (var id in data) {
loadedTask.push({
key: id,
text: data[id].text,
isComplete: data[id].isComplete,
});
}
setTaskItems(loadedTask);
} catch (error) {
setError(error.message);
}
};
this is how i call the useEffect
useEffect(() => {
handleCreateUser();
}, []);
The first thing I see is that you are not using await correctly. It should be before fetchDatabase(); function that is inside handleCreateUser like so:
await fetchDatabase();
The word await is there when you have to call an asynchronous function and you have to wait for this function to be completed.
Edit
To use only one useEffect you can check if your fetch function received your data by:
// or whatever statusCode you get when the data are present
if(reponse.statusCode === 200) {
// the await is not needed because it is present for the reponse abov
const data = response.json();
// looping Map/Object dengan key sebagai indexnya
const loadedTask = [];
for (var id in data) {
loadedTask.push({
key: id,
text: data[id].text,
isComplete: data[id].isComplete,
});
}
setTaskItems(loadedTask);
}
i got the answer, by using 2 useEffect
useEffect(() => {
handleCreateUser();
}, []);
useEffect(() => {
fetchDatabase();
}, [userId]);

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()

firebase query with where getting FirebaseError

I'm using "firebase": "^9.9.0", so web version 9 syntax. I'm trying to get a document by its field value, and im having no luck. I can get all the documents in the collection but every way I try to use .where I get errors, this is the main error i get:
FirebaseError: Expected type 'ba', but it was: a custom Pa object.
Here is my latest attempt:
const router = useRouter()
const paletteData = router.query
const [palette, setPalette] = useState([])
useEffect(() => {
const docRef = query(collection(db, 'palettes'), where("slug", "==", "03045e-0077b6-00b4d8-90e0ef-caf0f8"));
const docSnap = getDoc(docRef)
if (docSnap.exists()) {
setPalette(docSnap.data())
console.log("Document data:", docSnap.data());
} else {
console.log("No such document!");
}
}, [router])
Another attempt was:
useEffect(() => {
getDoc(doc(db, "palettes"), where("slug", "==", paletteData.palette)).then(docSnap => {
if (docSnap.exists()) {
console.log("Document data:", docSnap.data());
} else {
console.log("No such document!");
}
})
}, [router])
Note, slug is a field inside of the document in the palette collection.
Might not be the best solution but after messing around, I got this to work:
const { user } = params
const userData = []
const userRef = collection(db, 'users')
const q = query(userRef, where('uid', '==', user), limit(1))
const userDoc = await getDocs(q)
userDoc.forEach((doc) => {
userData.push(doc.data())
})
console.log(userData)

Deletion in FireStore (Latest Snip)

I have a Data Table i want to delete every document inside collection before invoke loadCheckOut.
How can i dow that with latest JS Syntax.
I am using React JS, and it initilize DB from getDb() method so methods like db.collection() not work on it i want a complete moduler solution
const loadCheckout = async (priceId) => {
//before adding we must delete existing collection
const docRef_x = collection(db, `customers/${user.uid}/checkout_sessions`);
const snapshot = await getDocs(docRef_x);
const x = await deleteDoc(snapshot);
const docRef = await addDoc(
collection(db, `customers/${user.uid}/checkout_sessions`),
{
price: priceId,
success_url: window.location.origin,
cancel_url: window.location.origin,
}
);
const ref = collection(db, `customers/${user.uid}/checkout_sessions`);
const snap = onSnapshot(
ref,
{ includeMetadataChanges: true },
async (doc) => {
var error = null,
sessionId = null;
var first = true;
doc.forEach((ele) => {
if (first) {
error = ele.data().error;
sessionId = ele.data().sessionId;
first = false;
}
});
console.log(sessionId);
if (error) {
alert(error);
}
if (sessionId) {
const stripe = await loadStripe(stripe_public_key);
stripe.redirectToCheckout({ sessionId });
}
}
);
};
This won't work:
const snapshot = await getDocs(docRef_x);
const x = await deleteDoc(snapshot);
The deleteDoc function requires a single DocumentReference, while your snapshot is a QuerySnapshot. This has very little to do with the change in syntax, as snapshot.delete() also wouldn't have worked in v8 of the SDK and earlier.
To delete the documents in the query snapshot, loop over the results and delete them one by one:
snapshot.forEach((doc) => {
deleteDoc(doc.ref);
});

Firestore query having problem with getting the data I need on react native

await firebase
.firestore()
.collection("patients")
.doc(firebase.auth().currentUser.uid)
.collection("appointment")
.orderBy("dateTime", "asc")
.get()
.then((result) => {
let appointment = result.docs.map((doc) => {
const data = doc.data();
const id = doc.id;
const currentDate = moment().format("YYYY-MM-DD");
const dt=new moment(doc.data().dateTime,'YYYY-MM-DD').format('YYYY-MM-DD');
if(dt===currentDate){
return { id, ...data };
}
});
dispatch({ type: GET_APPOINTMENT, appointment });
});
I am trying to do 3 things at once:
Get all the appointments for today in an object
Get all appointments after today in an object
When an appointment passes the current time, I want to delete it from the database
How can it be done?
What you can do is get all the documents in the user collection in a single query, then go through each document and sort it based on whether it is in the past, today, or in the future.
Here is an example of this, with comments explaining what's going on.
const db = firebase.firestore();
db
.collection("patients")
.doc(firebase.auth().currentUser.uid)
.collection("appointment")
.orderBy("dateTime", "asc")
.get()
.then(async (result) => {
const pastAppointmentRefs = [], todayAppointments = [], futureAppointments = [];
// Get current date in YYYY-MM-DD and milliseconds, just the once
const currentDate = moment().format("YYYY-MM-DD");
const currentDateMs = Date.now();
result.forEach((doc) => {
// Get appointment datetime and it's equivalent in milliseconds
const dateTime = doc.get("dateTime"); // format: YYYY-MM-DD-HH:mm
const dateTimeMs = moment(dateTime, 'YYYY-MM-DD-HH:mm').valueOf();
// sort appointment based on dateTime
if (dateTimeMs < currentDateMs) {
// is in the past, could also be currently taking place
pastAppointmentRefs.push(doc.ref);
} else if (currentDate === dateTime.slice(0,10)) {
// is today
todayAppointments.push({ id: doc.id, ...doc.data() });
} else {
// is in the future
futureAppointments.push({ id: doc.id, ...doc.data() });
}}
});
// if there are any found appointments in the past, delete them all
if (pastAppointmentRefs.length > 0) {
const batch = db.batch();
// warning! you can only delete up to 500 docs in a single batch
pastAppointmentRefs.forEach((ref) => {
batch.delete(ref);
});
await batch.commit(); // commit the changes (the deletions)
}
// fire the event
dispatch({
type: GET_APPOINTMENTS,
today: todayAppointments,
future: futureAppointments
});
});

Resources