Tuesday, 23 April 2019

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)
    {channelObj}
etc

I've successfully attached a listener to the sub collection messages although I am having trouble detaching that listener, so when I switch from and to chat channels I get duplicate entries as the listeners keep stacking.

Here's the script block from my vue file

<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: async function(newValue, oldValue) {
      this.messages = []
      oldValue && 
        await this.detachListeners(newValue, oldValue)
      await this.unsubscribe
      await this.timeout(2000)
      await this.addListeners(newValue)
    },
  },
  methods: {
    addListeners(newValue) {
      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)
    },
    detachListeners(newValue, oldValue) {
      this.unsubscribe = 
      this.channelsRef
        .doc(oldValue.id)
        .collection('messages')
        .onSnapshot(() => {})
      //
      console.log('[detachListeners] channel:', oldValue.id)
    },
    timeout(ms) {
      console.log('waiting...')
      return new Promise(resolve => setTimeout(resolve, ms));
    },
  },
}
</script>


As you can see I am using a Vue watcher to monitor when the channel changes. To clarify, the console.log are firing with the correct doc ids so it should be targeting correctly. I tried using asynchronous code to await the detach but that does not work.

The docs advising saving the detach code to a variable and calling that, which I am now doing in my watch block. When console logging that it says this

ƒ () {
            asyncObserver.mute();
            firestoreClient.unlisten(internalListener);
        }

So I am a bit lost here, seems I am targeting the right collection with the right method for unlistening ... any other steps I can take to debug?



from Problem detaching listener from Firestore nested / sub-collection

No comments:

Post a Comment