Skip to content Skip to sidebar Skip to footer

Problem Detaching Listener From Firestore Nested / Sub-collection

My scenario is a chat app with the following setup in Firestore channels (collection) id (doc) messages (collection) {channelObj} id (doc) messages (collection)

Solution 1:

You have to store in data the function returned by the onSnapshot() method and call this function in order to detach the listener.

In your existing code you are indeed declaring an unsubscribe object in data but you are not correctly assigning to it the function returned by the onSnapshot() method (you should do that in the addListeners() method) and you are not calling it correctly (you do this.unsubscribe instead of this.unsubscribe()).

I've not reproduced your full case, since it implies a Vuex store and some extra components but you will find below a similar code that demonstrates how it works (my settings are a bit different than yours -I use require("../firebaseConfig.js"); and fb.db.collection(channel)- but you'll easily get the philosophy!):

<template>
  <div>
    <input v-model="currentChannel" placeholder="Enter Current Channel">
    <p>CurrentChannel is: {{ currentChannel }}</p>
    <div class="messagesList">
      <li v-for="m in messages">{{ m.name }}</li>
    </div>
  </div>
</template>

<script>
const fb = require("../firebaseConfig.js");
export default {
  data() {
    return {
      messages: [],
      currentChannel: null,
      listener: null    //We store the "listener function" in the object returned by the data function
    };
  },
  watch: {
    currentChannel: function(newValue, oldValue) {
      this.messages = [];
      if (this.listener) {
        this.listener();  //Here we call the "listener function" -> it detaches the current listener
        this.addListeners(newValue);
      } else {
        this.addListeners(newValue);
      }
    }
  },
  methods: {
    addListeners(channel) {
      this.listener = fb.db.collection(channel).onSnapshot(snapshot => {
        snapshot.docChanges().forEach(change => {
          if (change.type == "added") {
            let doc = change.doc;
            this.messages.push({
              id: doc.id,
              name: doc.data().name
            });
          }
        });
      });
    }
  }
};
</script>

<style>
.messagesList {
  margin-top: 28px;
}
</style>

So, if we try to apply this approach to your code, the modified code would be as follows:

<script>
import firestore from 'firebase/firestore'
import { mapGetters } from 'vuex'
import SingleMessage from './SingleMessage'
import MessageForm from './MessageForm'

export default {
  name: 'messages',
  components: {
    SingleMessage,
    MessageForm,
  },
  data() {
    return {
      channelsRef: firebase.firestore().collection('channels'),
      messages: [],
      channel: '',
      unsubscribe: null
    }
  },
  computed: {
    ...mapGetters(['currentChannel']),
  },
  watch: {
    currentChannel: function(newValue, oldValue) {
          this.messages = [];
          if (this.unsubscribe) {
            this.unsubscribe(); 
            this.addListeners(newValue);
          } else {
            this.addListeners(newValue);
          }
    }
  },
  methods: {
    addListeners(newValue) {
      this.unsubscribe = this.channelsRef
        .doc(newValue.id)
        .collection('messages')
        .onSnapshot(snapshot => {
          snapshot.docChanges().forEach(change => {
            if (change.type == 'added') {
              let doc = change.doc
              this.messages.push({
                id: doc.id,
                content: doc.data().content,
                timestamp: doc.data().timestamp,
                user: doc.data().user,
              });
            }
          });
        });
      console.log('[addListeners] channel:', newValue.id)
    }
  }
}
</script>

Post a Comment for "Problem Detaching Listener From Firestore Nested / Sub-collection"