'How do I get all documents in a Cloud Firestore collection using Version 9 of the Modular Web SDK?
I am trying to get all documents in a collection using version 9 of the Web SDK. But I am getting this error:
"TypeError: querySnapshot.map is not a function"
This is the component where I get the error:
import { useEffect, useState } from "react";
import { collection, getDocs } from "firebase/firestore";
import { db } from "../../firebase";
function CurrentUser() {
const [names, setNames] = useState([]);
async function getMakers() {
const querySnapshot = await getDocs(collection(db, "users"));
querySnapshot.map((doc) => {
setNames((doc.id = doc.data()));
});
}
getMakers();
return <div>{names.map((doc) => doc.firstName)}</div>;
}
export default CurrentUser;
Solution 1:[1]
querySnapshot
is an instance of a QuerySnapshot
object, not a JavaScript Array. This means it doesn't have the normal Array methods like map()
, some()
and includes()
. However, it does have its own version of a forEach()
method that can be used to iterate over the entries in the snapshot. If you need to access methods of a normal array, you can use the snapshot's docs
property instead (which internally calls forEach()
to assemble an array).
To correctly fetch the documents in the array, optionally plucking the first names of each returned document, you can use any of the following strategies:
- Option 1:
useState
anduseEffect
for each user's complete data
import { useEffect, useState } from "react";
// ...
const [userDataArray, setUserDataArray] = useState([]);
useEffect(() => {
let unsubscribed = false;
getDocs(collection(db, "users"))
.then((querySnapshot) => {
if (unsubscribed) return; // unsubscribed? do nothing.
const newUserDataArray = querySnapshot.docs
.map((doc) => ({ ...doc.data(), id: doc.id }));
setUserDataArray(newUserDataArray);
})
.catch((err) => {
if (unsubscribed) return; // unsubscribed? do nothing.
// TODO: Handle errors
console.error("Failed to retrieve data", err);
});
return () => unsubscribed = true;
}, []);
return (
<div>
{ userDataArray.map((userData) => userData.firstName) }
</div>
);
- Option 2:
useState
anduseEffect
for just each user'sfirstName
import { useEffect, useState } from "react";
// ...
const [firstNamesArray, setFirstNamesArray] = useState([]);
useEffect(() => {
let unsubscribed = false;
getDocs(collection(db, "users"))
.then((querySnapshot) => {
if (unsubscribed) return; // unsubscribed? do nothing.
const newFirstNamesArray = querySnapshot.docs
.map((doc) => doc.get("firstName"));
setFirstNamesArray(newFirstNamesArray);
// need to remove duplicates? use this instead:
// const firstNamesSet = new Set();
// querySnapshot
// .forEach((doc) => firstNamesSet.add(doc.get("firstName")));
//
// setFirstNamesArray([...firstNamesSet]);
})
.catch((err) => {
if (unsubscribed) return; // unsubscribed? do nothing.
// TODO: Handle errors
console.error("Failed to retrieve data", err);
});
return () => unsubscribed = true;
}, []);
return (
<div>
{ firstNamesArray }
</div>
);
- Option 3: Make use of a tree-shakeable utility library like
react-use
to handle intermediate states.
import { useAsync } from 'react-use';
// ...
const remoteUserData = useAsync(
() => getDocs(collection(db, "users"))
);
if (remoteUserData.loading)
return (<div>Loading...</div>);
if (remoteUserData.error) {
console.error("Failed to load data! ", remoteUserData.error);
return (<div class="error">Failed to load data!</div>);
}
const userDataArray = remoteUserData.value;
return (
<div>
{ userDataArray.map((userData) => userData.firstName) }
</div>
);
Solution 2:[2]
useEffect(() =>
onSnapshot(collection(db, 'posts'),
snapshot => {
setPosts(
snapshot.docs.map(doc => ({
id: doc.id,
...doc.data()
}))
)
})
, [])
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Rukkiecodes |