Home > Software engineering >  Nested DocumentSnapshot onSnapshot doesn't yield results
Nested DocumentSnapshot onSnapshot doesn't yield results

Time:12-30

I'm querying a Firestore document for the contents of its data. One of the fields, listField, is an array of DocumentReferences to other documents. As such, I have nested snapshots: One for the original document, then a loop for all of the documents referenced in listField.

When I try to add all of this into data, the list data always returns empty. With some testing, I've found that the list is always undefined outside of the inner docSnapshot loop, but is correct within that loop.

For what it's worth, I've tested this with the inner loop using getDoc as well as with onSnapshot, and both yield the same results. Any idea why the list in the inner loop doesn't populate?

const [data, setData] = useState<DataType>();
useEffect(() => {
  const getData = async () => {
    var docRef = doc(firestore, "/collection/documentId123");
    onSnapshot(docRef, (docSnapshot) => {
      var list: ListType[] = [];
      docSnapshot.data()?.listField.forEach(async (docRef2: DocumentReference) => {
        const doc2 = await getDoc(docRef2);
        list.push({
          id: docRef2.id,
          field: doc2.get("field"),
        });
        // console.log(list) WORKS HERE!
      });
      // console.log(list) IS UNDEFINED HERE!
      setData({
        title: docSnapshot.data()?.title,
        otherField: docSnapshot.data()?.otherField,
        list: list,
      });
    });
  };
  getData();
}, []);

CodePudding user response:

The setData() might run before all promises are resolved. You can use Promise.all() instead of using async function in forEach loop. Try refactoring the code as shown below:

const [data, setData] = useState<DataType>({});

useEffect(() => {
  const getData = async () => {
    var docRef = doc(firestore, "/collection/documentId123");

    onSnapshot(docRef, async (docSnapshot) => {
      const snapshots = await Promise.all(docSnapshot.data()?.listField.map(r) => getDoc(r))
      const list = snapshots.map((s) => ({
        id: s.id,
        field: s.get("field")
      }))

      setData({
        title: docSnapshot.data()?.title,
        otherField: docSnapshot.data()?.otherField,
        list: list,
      });
    });
  };

  getData();
}, []);
  • Related