'How to update a Vue ref using a Watch inside a composable?

I have a Vue composable named getDocument that gets a Firebase document.

The code inside onSnapshot callback runs asynchronously. And I'm trying to update the document and error refs with values returned from onSnapshot.

But I want to avoid using a Watch outside the getDocument if possible, because always having to wrap my code in a Watch is a pain.

Instead I want to put a Watch inside the getDocument.ts and have it update the document and error refs there.

This is what I have so far, with no Watch inside getDocument.ts.

src/composable/getDocument.ts

import { ref, watchEffect, watch } from 'vue';
import { db } from 'src/firebase/config';
import {
  doc,
  onSnapshot,
  DocumentSnapshot,
  DocumentData,
} from 'firebase/firestore';

const getDocument = (collectionString: string, documentId: string) => {
  const error = ref<string | undefined>();
  const document = ref<DocumentData | undefined>();

  const docRef = doc(db, collectionString, documentId);
  const unsubscribe = onSnapshot(
    docRef,
    (doc: DocumentSnapshot<DocumentData>) => {
      if (doc.data()) {
        document.value = {
          ...doc.data(),
          id: doc.id,
        };
        error.value = undefined;
      } else {
        error.value = "That document doesn't exist";
      }
    },
    (err) => {
      console.log(err.message);
      error.value = 'Could not fetch documents';
    }
  );
  // Cancel the listener when composable not in use
  watchEffect((onInvalidate) => {
    onInvalidate(() => {
      unsubscribe();
    });
  });
  // Maybe use a "Watch" here to update the doucment and error refs? But I can't get it working.
  return { document, error };
};

export default getDocument;

Now when importing the getDocument composable I could wrap everything in a Watch to make sure the ref has a value. But I would rather do that inside getDocument instead.

For example:

src/composable/anotherComposable.ts

import getDocument from 'src/composables/getDocument';
const { document, error } = getDocument('users', 'USER_ID_HERE');

// I could wrap all my code here in a Watch, but I was hoping to avoid that. I want to use the Watch inside the getDocument composable to do the same thing.
watch(document, () => {
    console.log(document.value);
  });

// This is how I would like to ultimately use the document ref after the Watch is moved inside the getDocument composable. Currently this will show as undefined. So I need to somehow put a Watch inside the getDocument composable to make this have a value.
console.log(document.value);


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source