skip to Main Content

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?

2

Answers


  1. 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);
          });
    
        });
      }, []);
    
    Login or Signup to reply.
  2. 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

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search