Thingamajig
Thingamajig

Reputation: 4467

Add data to nested foreach array using React and Firebase/Firestore

I have a few nested called when using Firestore. With these, they rely on the use of the .then() because they are network calls. They appear to be so nested that they stop having access to the list that I am looking to update called "messages."

Because of this mess, I am updating state every iteration of the loop which is wildly non performant.

How can I push to the messages array given this as a starting point?:

BTW, this method onCollectionUpdate is bound in my constructor like this.onCollectionUpdate = this.onCollectionUpdate.bind(this). Thank you!

onCollectionUpdate = querySnapshot => {
    const messages = [];
    querySnapshot.forEach(doc => {
      const { _id, text, createdAt, user } = doc.data();
      var userId = user.id
      var userRef = firebase.firestore().collection('users').doc(userId)
      userRef.get().then(data => {
        var user = data.data()
        var userObject = {
            _id: userId,
            name: user.name,
            avatar: user.profilePictureURL
        }
        var newMessage = {
          _id,
          text,
          createdAt: new Date(createdAt.seconds),
          user: userObject
        }
        this.setState(prevState => ({messages: [...prevState.messages,newMessage]}))

        messages.push() //Not working. By time it reaches set state below (outside of the loops) it is empty.
      })
    })
    this.setState({
      messages,
      loading: false,
    });
  }

Upvotes: 1

Views: 592

Answers (1)

Daniel Jee
Daniel Jee

Reputation: 664

Here's a solution

onCollectionUpdate = querySnapshot => {
    var promises = [];
    const messages = [];

    querySnapshot.forEach(doc => {
      const { _id, text, createdAt, user } = doc.data();
      var userId = user.id
      var userRef = firebase.firestore().collection('users').doc(userId)
      promises.push(
        userRef.get()
        .then(data => {
          var user = data.data()
          var userObject = {
            _id: userId,
            name: user.name,
            avatar: user.profilePictureURL
          }
          return {
            _id,
            text,
            createdAt: new Date(createdAt.seconds),
            user: userObject
          }
        })
      )
    })

    Promise.all(promises).then(newMessages=> {
        this.setState(prevState => {
            return {
                messages: [...prevState.messages, ...newMessages],
                loading: false,
              }
        })
    })
  }

Once all you get all the responses, you call setState once. Hope this is what you wanted :)

Upvotes: 2

Related Questions