I have a simple React App using Firestore. I have a document in Firestore:
{
date: November 20, 2022 at 11:24:44 AM UTC 1,
description: "...",
title: "Dummy title",
type: "student",
userRef: /users/0AjB4yFFcIS6VMQMi7rUnF3eJXk2
}
Now I have a custom hook, that fetches the data:
export const useAnnouncements = () => {
const [announcements, setAnnouncements] = useState([]);
useEffect(() => {
getAnnouncements().then((documents) => {
const documentsList = [];
documents.forEach((doc) => {
const document = { id: doc.id, ...doc.data() };
getUser(document.userRef).then((u) => {
document.user = u.data(); // <-- HERE is problem
});
documentsList.push(document);
setAnnouncements(documentsList);
});
});
}, []);
return [announcements];
};
Problem is that I have a REFERENCE field type, and it has to be fetched separately. Result? My list is populated, but first without user. Later, when the users' data is fetched, the state is not being updated.
How to deal with React Firestore's reference field?
CodePudding user response:
Array.prototype.forEach is not designed for asynchronous code. (It was not suitable for promises, and it is not suitable for async-await.) instead you can use map.
useEffect(() => {
getAnnouncements().then((documents) => {
const promises = documents.map((doc) => {
return getUser(doc.userRef).then((u) => {
const document = { id: doc.id, user: u.data(), ...doc.data() };
return document;
});
});
Promise.all(promises).then((documentsList) => {
setAnnouncements(documentsList);
});
});
}, []);
CodePudding user response:
I think you need to wait for all the data to be fetched
export const useAnnouncements = () => {
const [announcements, setAnnouncements] = useState([]);
useEffect(() => {
let isValidScope = true;
const fetchData = async () => {
const documents = await getAnnouncements();
if (!isValidScope) { return; }
const allPromises = documents?.map(doc => {
return getUser(doc.userRef)
.then(user => {
return {
id: doc.id,
...doc.data(),
user: user.data()
}
})
}
const documentsList = await Promise.all(allPromises);
if (!isValidScope) { return; }
setAnnouncements(documentsList);
}
fetchData()
return () => { isValidScope = false }
}, []);
return [announcements];
};
Hope it helps in some way