'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:
useStateanduseEffectfor 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:
useStateanduseEffectfor 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-useto 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 |
