title: How to unsubscribe from a Firestore stream using the Vue onInvalidate callback?
body: <p>I am using Vue 3 to create a Firebase composable that subscribes to an <code>onSnapshot()</code> stream.</p>
  <p>And I am trying to unsunscribe from that stream by calling the returned <code>unsubscribe()</code> function inside <code>watchEffect</code> and <code>onInvalidate</code>. <a href="https://v3.vuejs.org/guide/reactivity-computed-watchers.html#side-effect-invalidation" rel="nofollow noreferrer">Here are the Vue docs that explain those particular functions</a>.</p>
  <p>Using a <code>console.log()</code> I can confirm my <code>watchEffect</code> is firing.</p>
  <p>But I cannot trigger my second <code>console.log()</code> that exists inside <code>onInvalidate</code>, and therefore I'm not sure if the <code>unsubscribe()</code> function is ever called.</p>
  <p>In the code below, why doesn't my <code>console.log(&quot;unsubscribe fired&quot;);</code> ever fire?</p>
  <p>How else can I be sure I am unsubscribed from the Firestore stream? Is there a log of this inside the Firebase dashboard or something similar?</p>
  <p>Here is the Vue Firestore composable (it is written in TypeScript):</p>
  <pre><code>import { ref, reactive, watchEffect } from &quot;vue&quot;;
  import { projectFirestore } from &quot;@/firebase/config&quot;;
  import {
    query,
    orderBy,
    onSnapshot,
    DocumentData,
    collection,
  } from &quot;firebase/firestore&quot;;
  
  const useCollection: any = (col: any) =&gt; {
    const documents = reactive&lt;Record&lt;string, unknown&gt;[]&gt;([]);
    const error = ref&lt;string | null&gt;(null);
    // references
    const collectionReference = collection(projectFirestore, col);
    const collectionOrdered = query(
      collectionReference,
      orderBy(&quot;createdAt&quot;, &quot;desc&quot;)
    );
    // updates
    const unsubscribe = onSnapshot(
      collectionOrdered,
      (snapshot) =&gt; {
        snapshot.docs.forEach((doc: DocumentData) =&gt; {
          documents.push({ ...doc.data(), id: doc.id });
        });
        error.value = null;
        console.log(documents);
      },
      (err) =&gt; {
        console.log(err.message);
        documents.splice(0);
        error.value = err.message;
      }
    );
    watchEffect((onInvalidate) =&gt; {
      console.log(&quot;watchEffect fired&quot;); //This fires
      onInvalidate(() =&gt; {
        unsubscribe();
        console.log(&quot;unsubscribe fired&quot;); //This does not fire
      });
    });
    return { documents, error };
  };
  
  export default useCollection;
  </code></pre>