2021-12-05

How do I merge the results of two Firestore Realtime listeners into one array of objects using React

I am trying to merge the data received from two real-time listeners into one requests array. The array contains objects as its elements. This is so I can pass one single requests prop and manipulate that in my Dashboard component.

In Firestore, my documents in my Requests collection contain employeeUser and managerUser as references to another Users collection which contains information about the user such as their username. So I am also trying to receive their username within my snapshot as well.

Here is an example of the fields in the document of the Requests collection: Example fields in a document of my Requests collection

I am having issues merging the data from both listeners into one requests array using useEffect and useState hooks. Also, when I try to use the spread syntax to merge arrays it doesn't seem to actually have an effect on setRequests. When I use a callback setRequests((prev) => [...prev, ...employeeRequests]) it doesn't seem to do anything either.

In a nutshell, with the current code I have below, I seem to be receiving the correct data in the console (only in managerRequests & employeeRequests arrays) but I cannot merge the two arrays together and it is not displaying properly after all renders have been completed.

I did also notice that while I am editing the code and make a change right after that involves requests, the hot reload re-renders the page once more and then everything displays correctly. Once I refresh the page, nothing displays again until I have another hot reload that affects setRequests. I am wondering if I have to trigger some more renders somehow to get all the updates in the useEffect dependency array.

Here is my code so far:

  const [requests, setRequests] = useState([]); // the array where I am trying to merge both managerRequests and employeeRequests into (cause its the only way I've been able to get it to kinda work)
  const [managerRequests, setManagerRequests] = useState([]);
  const [employeeRequests, setEmployeeRequests] = useState([]);
  
  useEffect(() => {
    if (firebase) {
        
        //Set up first listener
      const unsubscribe = firebase.getManagerRequests({
        userUid: user.uid, onSnapshot: (snapshot) => {
          const managerRequests = [];
          snapshot.forEach(async doc => {

            const [managerPromise, employeePromise] = await Promise.all([doc.data().managerUser.get(), doc.data().employeeUser.get()]); // single get() queries to retrieve employee and manager usernames from referenced User collection
            var managerUsername = managerPromise.data().username;
            var employeeUsername = employeePromise.data().username;

            managerRequests.push({
              id: doc.id,
              managerUsername: managerUsername,
              employeeUsername: employeeUsername,
              ...doc.data()
            })

          })

          // console.log(managerRequests) -> managerRequests contains the correct set of data in an array of objects ex: [{id:1, ...}, {id:2, ...}]
          setManagerRequests(managerRequests);
          setLoadingElements((vals) => ({ ...vals, managerRequestsLoading: false })) // Currently not using these as barrier flags
        }
      })
      
      //Set up second listener
      const unsubscribetwo = firebase.getEmployeeRequests({
        userUid: user.uid, onSnapshot: (snapshot) => {
          const employeeRequests = [];
            snapshot.forEach(async doc => {
              const [managerPromise, employeePromise] = await Promise.all([doc.data().managerUser.get(), doc.data().employeeUser.get()]);
              var managerUsername = managerPromise.data().username;
              var employeeUsername = employeePromise.data().username;

              employeeRequests.push({
                id: doc.id,
                managerUsername: managerUsername,
                employeeUsername: employeeUsername,
                ...doc.data()
              })

            })    
          // console.log(employeeRequests) > employeeRequests contains the correct set of data in an array of objects ex: [{id:1, ...}, {id:2, ...}]
          setEmployeeRequests(employeeRequests);
          setLoadingElements((vals) => ({ ...vals, employeeRequestsLoading: false })) // Currently not using these as barrier flags
        }
      })

      setRequests([...managerRequests, ...employeeRequests]); // This does not seem to work. requests has nothing in it in the last render

      return () => {
        if (unsubscribe) {
          unsubscribe();
        }
        if (unsubscribetwo) {
          unsubscribetwo();
        }
      }

    }
  }, [firebase, user])
  
  
  console.log(requests) // contains nothing in requests
  
  return (
    <>
      <Navbar />
      <Dashboard requests={requests} /> // This is where I want to pass an array of the merged employee and manager requests as a prop to my Dashboard component
      <Footer />
    </>
  )
  

Edit: Here is how I fixed it with the help of the first answer:

  const [requests, setRequests] = useState([]); 
  const [managerRequests, setManagerRequests] = useState([]);
  const [employeeRequests, setEmployeeRequests] = useState([]);
  const [managerUsername, setManagerUsername] = useState('');
  const [employeeUsername, setEmployeeUsername] = useState('');
  const [managerUsernameEmployeeSide, setManagerUsernameEmployeeSide] = useState('');
  const [employeeUsernameEmployeeSide, setEmployeeUsernameEmployeeSide] = useState('');
  
  useEffect(() => {
    if (firebase) {
        
        //Set up first listener
      const unsubscribe = firebase.getManagerRequests({
        userUid: user.uid, onSnapshot: (snapshot) => {
          const managerRequests = [];
          snapshot.forEach(doc => {
            // Had to remove await Promise.all(...) to get it to work
            
            doc.data().managerUser.get()
              .then(res => {
                setManagerUsername(res.data().username);
              })
              
             doc.data().employeeUser.get()
              .then(res => {
                setEmployeeUsername(res.data().username);
              })

            managerRequests.push({
              id: doc.id,
              managerUsername: managerUsername,
              employeeUsername: employeeUsername,
              ...doc.data()
            })

          })

          setManagerRequests(managerRequests);
        }
      })


      return () => {
        if (unsubscribe) {
          unsubscribe();
        }
      }

    }
  }, [firebase, user, managerUsername, employeeUsername])
  
  useEffect(() => {
    if (firebase) {
        //Set up second listener
      const unsubscribetwo = firebase.getEmployeeRequests({
        userUid: user.uid, onSnapshot: (snapshot) => {
          const employeeRequests = [];
            snapshot.forEach(doc => {
              const [managerPromise, employeePromise] = await Promise.all([doc.data().managerUser.get(), doc.data().employeeUser.get()]);
              var managerUsername = managerPromise.data().username;
              var employeeUsername = employeePromise.data().username;
              
              doc.data().managerUser.get()
              .then(res => {
                setManagerUsernameEmployeeSide(res.data().username);
              })
              
             doc.data().employeeUser.get()
              .then(res => {
                setEmployeeUsernameEmployeeSide(res.data().username);
              })

              employeeRequests.push({
                id: doc.id,
                managerUsername: managerUsernameEmployeeSide,
                employeeUsername: employeeUsernameEmployeeSide,
                ...doc.data()
              })

            })    

          setEmployeeRequests(employeeRequests);

        }
      })
    }
    return () => {
        if (unsubscribetwo) {
          unsubscribetwo();
        }
    }
  }, [firebase, user, managerUsernameEmployeeSide, employeeUsernameEmployeeSide]
  
    useEffect(() => {
    if (managerRequests.length > 0 && employeeRequests.length > 0) {
      setTransactions([...managerRequests, ...employeeRequests]);
    }
  }, [managerRequests, employeeRequests])
  
  return (
    <>
      <Navbar />
      <Dashboard requests={requests} /> // Requests finally receives one array with all values from managerRequests and employeeRequests combined
      <Footer />
    </>
  )


from Recent Questions - Stack Overflow https://ift.tt/31dff1U
https://ift.tt/3IjtJ1e

No comments:

Post a Comment