I want to access a collection inside a document. I want to access the document in the posts collection that has a specific postId however, inside that document there is a collection called comments which I would like to access. I don't think the code that I did is correct though.
const fetchComments = async() => {
const posts = doc(db, 'posts', postId);
const comment_list = collection(posts, "comments");
const snapshot = await getDocs(comment_list);
setComments(snapshot.docs.map((doc) => {
doc.data();
}));
}
CodePudding user response:
Inside your map
function, you aren't returning the document's data.
setComments(snapshot.docs.map((doc) => {
doc.data(); // <-- not returned!
}));
This means for as many comments as you have on a post, you'll just get an empty array of that length. i.e. 3 comments -> setComments([undefined, undefined, undefined])
You can fix this by rewriting that instruction as one of:
setComments(snapshot.docs.map((doc) => {
return doc.data()
}));
setComments(snapshot.docs.map(
(doc) => doc.data()
));
setComments(snapshot.docs.map((doc) => doc.data()))
However, as you are dealing with an array of comments, you should also return the document ID of each comment in the data so you can use it as the key
for rendering.
setComments(snapshot.docs.map(
(doc) => ({ key: doc.id, ...doc.data() })
));
Other notes
Instead of using both doc()
and collection()
:
const posts = doc(db, "posts", postId);
const comment_list = collection(posts, "comments");
you could use just collection()
on its own:
const comment_list = collection(db, "posts", postId, "comments");
This turns your function into:
const fetchComments = async () => {
const comment_list = collection(db, "posts", postId, "comments");
const snapshot = await getDocs(comment_list);
setComments(snapshot.docs.map(
(doc) => ({ key: doc.id, ...doc.data() })
));
}
To make it safer for use in React, you should wrap it in a useEffect
to make sure that calling setComments
is still valid once you have the data.
useEffect(() => {
let unsubscribed = false;
if (!postId) {
console.error(`Failed to load post comments! postId (${postId}) was falsy`);
setComments(null);
setLoading(false);
setErrorMessage("Invalid post ID");
return;
}
const comment_list = collection(db, "posts", postId, "comments");
setLoading(true);
getDocs(comment_list)
.then((comment_list_snapshot) => {
if (unsubscribed) return; // silently discard, outdated
setComments(comment_list_snapshot.docs.map(
(doc) => ({ key: doc.id, ...doc.data() })
));
setErrorMessage(null);
setLoading(false);
})
.catch((err) => {
if (unsubscribed) return; // silently discard, outdated
console.error("Failed to load post comments!", err);
setComments(null);
setErrorMessage("Comments could not be loaded.");
setLoading(false);
});
return () => unsubscribed = true;
}, [db, postId]);
As the above block looks the same for most Firestore calls aside from what data you are setting consider using a Firebase hooks library or write your own so you can flatten it all to something like:
const [ comments_docs, comments_loading, comments_error ] = useFirestoreCollection(db, "posts", postId, "comments")
if (comments_loading)
return null; // hide until loaded
if (comments_error) {
console.error("Failed to load comments: ", comments_error);
return (<div class="error">Could not load comments</div>)
}
const comments = comments_docs.map((doc) => ({ key: doc.id, ...doc.data() }));
return ({
comments.map((commentData) => {
return <Comment
key={commentData.key}
author={commentData.author}
content={commentData.content}
...
/>
})
});
CodePudding user response:
According to your problem first collection is posts, id is staring with 228... and subcollection is comments. Try out the code below by replacing your values.
db.collection('FirstCollection/' id '/DocSubCollectionName').get().then((subCollectionSnapshot) => {
subCollectionSnapshot.forEach((subDoc) => {
console.log(subDoc.data());
});
});
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>