How to listen firebase real-time changes into Reactjs?

Viewed 2616

I am creating a real-time chat application using firebase and React js. I create a const functions = require('firebase-functions'); called "chats" inside the firebase. This collection contains unique room_ID(a combination of sender and receiver) and that document again contains subcollections called "messages". Each collection inside the message has infomation like message, time, sender_id, and read status.

Now, every time, when I receive a new message into the chat list I have to update the conversation. I use componentDidMount() method of Reactjs and write below code:

firestore.collection('chats').doc("b7EuhNpdzXUlxhSgDkn2a6oReTl1_OZbrlH8FjmVFqSAtAc0A6TUHS3I3").collection("messages")
.onSnapshot(querySnapshot => {
  console.log("querySnapshot", querySnapshot)
  querySnapshot.docChanges().forEach(change => {
    console.log("change", change)
    if (change.type === 'added') {
      this.setState({messages : [...this.state.messages, change.doc.data()]});
      console.log('New city: ', change.doc.data());
    }
    if (change.type === 'modified') {
      console.log('Modified city: ', change.doc.data());
    }
    if (change.type === 'removed') {
      console.log('Removed city: ', change.doc.data());
    }
  });
});

You can see here that, It will only work for a single room(b7EuhNpdzXUlxhSgDkn2a6oReTl1_OZbrlH8FjmVFqSAtAc0A6TUHS3I3). I want to write query in such a way that it will listen to the message for each contact. For that, I have to remove the restriction of specific doc.

Please help me.

Thank you in advance.

enter image description here

Here is the structure of Firebase database.

enter image description here

enter image description here

3 Answers

I believe a few better approaches to fetch the data you want would be to either:

  • Restructure your Firestore to have only a messages collection, like the following example structure:

    messages collection
        uid
        receiverUserId
        senderUserId
        msg
        read
        time
    

With this approach you could filter the documents you are watching, for example documents received by the currently authenticated user from multiple users, by doing something like this:

firestore.collection("messages")
  .where("receiverUserId", "==", authUid)
  .onSnapshot(function(querySnapshot) {
     //do whatever
});
  • Create multiple listeners, one for each chats document, to watch it's subsequent messages subcollection. So you could do something like this untested code:

    firestore.collection('chats').get().then(function(querySnapshot) {
      querySnapshot.forEach(function(doc) {
          var eachChatRef = firestore.collection('chats').doc(doc.documentID)
          var messagesRef = eachChatRef.collection("messages");
          messagesRef.onSnapshot(function(snapshot) {
              snapshot.docChanges().forEach(function(messageDoc) {
                  // Do whatever
              });
          });
      });
    });
    

Look into the documentation for CollectionGroups - set your listener to the .collectionGroup("messages") - you will have to process through the changes for all of the different "chats" documents. (HINT: each returned messages DocRef includes the refPath field - which you can trivially parse to find the path to the "parent" chat document)

To extend to @LeadDreamer answer, this worked for me to listen to changes in all documents in a collection using collectionGroup:

const unsub = () =>
  onSnapshot(collectionGroup(db, "reservations"), (doc) => {
    doc.docs.forEach((d) => {
      console.log(d.data());
    });
  });
Related