How to properly initialize an empty collection when using `useCollection` inside a vue 3 ref?

88 Views Asked by At

I am trying to show a list on screen but for some reason Vue 3 (with vuefire) does not propagate the change, despite the network calls being done. Since I added username support, the screen keeps showing "No scenes created yet". Typescript complains about wrong typing, but I can't figure out how to properly fix it.

Edit: I am not trying to fix the typing, but rather to fix the collection not being updated when doing the following. The value keeps being an empty array despite:

 scenesCollection.value = useCollection(scenesCollectionRef);

Here are the two error for a document and a collection:

Type '_RefFirestore<UserInformation | undefined>' is not assignable to type 'null'. ts(2322)
Type '_RefFirestore<Scene[]>' is missing the following properties from type 'never[]': length, pop, push, concat, and 35 more. ts(2740)

Screenshot of the watchEffect callback

I tried manually specifying the ref typing just as I did for the userId, but to no avail. And anyway it would fix the ts error, but the code would still not work.

I submitted my code to ChatGPT as well but it considers it as valid code.

Do you have any other tricks I could pull? Is there a better way to change the code to keep auto updates from the server?

Here the entire code for the component:

<template>
  <div class="wrapper">
    <div v-if="!userDocument">
      <p>This user does not exist</p>
    </div>
    <div v-else-if="!scenesCollection">
      <p>This user does have public scenes</p>
    </div>
    <v-container v-else-if="scenesCollection.length" fluid>
      <v-row dense>
        <v-col v-for="scene in scenesCollection" :key="scene.id">
          <router-link :to="'/' + userId + '/' + scene.id" class="clickable">
            <v-card>
              <v-img
                :src="scene.coverUrl"
                class="align-end"
                gradient="to bottom, rgba(0,0,0,.1), rgba(0,0,0,.5)"
                height="200px"
                cover
              >
                <v-card-title class="text-white">{{
                  scene.friendlyName
                }}</v-card-title>
              </v-img>
            </v-card>
          </router-link>
        </v-col>
      </v-row>
    </v-container>
    <div v-else class="clickable">
      <br onload="alert('banana')" />
      <p>No scenes created yet</p>
    </div>
  </div>
</template>
import { ref, watchEffect } from 'vue';
import {
  useFirestore,
  useCurrentUser,
  useCollection,
  useDocument,
} from 'vuefire';
import { DocumentReference, collection, doc, getDoc } from 'firebase/firestore';
import { useRoute } from 'vue-router';

import type { Scene, UserInformation, UsernameInformation } from '@/types';

const db = useFirestore();
const route = useRoute();
const currentUser = useCurrentUser();
const username = route.params.username;
const isOnMePage = username === 'me';

const userId = ref<string | null>(null);

const fetchUserIdFromUsername = async (username: string) => {
  const docRef = doc(
    db,
    'usernames',
    username,
  ) as DocumentReference<UsernameInformation>;
  try {
    const usernameInfo = await getDoc(docRef);
    if (!usernameInfo.exists()) return '';
    return usernameInfo.data().uid;
  } catch (error) {
    console.error('Error fetching user ID:', error);
    return '';
  }
};

watchEffect(async () => {
  // Logic for determining userId
  if (isOnMePage && currentUser.value) {
    userId.value = currentUser.value.uid;
  } else if (typeof username === 'string') {
    userId.value = await fetchUserIdFromUsername(username);
  }
});

let userDocument = ref(null);
let scenesCollection = ref([]);

watchEffect(() => {
  console.log('Watching effect', { userId: userId.value });
  if (!userId.value) {
    userDocument.value = null;
    scenesCollection.value = [];
    return;
  }
  const userDocumentRef = doc(
    db,
    'users',
    userId.value,
  ).withConverter<UserInformation>({
    fromFirestore: (snapshot) => {
      // this avoids having `null` while the server timestamp is updating
      const data = snapshot.data({
        serverTimestamps: 'estimate',
      }) as UserInformation;
      console.log('New userInfo from firestore', { ...data, id: snapshot.id });
      return { ...data, id: snapshot.id };
    },
    toFirestore: (data: UserInformation) => data,
  });
  const scenesCollectionRef = collection(
    db,
    'users',
    userId.value,
    'scenes',
  ).withConverter<Scene>({
    fromFirestore: (snapshot) => {
      const data = snapshot.data({ serverTimestamps: 'estimate' }) as Scene;
      console.log('New scene from firestore', { ...data, id: snapshot.id });
      return { ...data, id: snapshot.id };
    },
    toFirestore: (data: Scene) => data,
  });

  userDocument.value = useDocument(userDocumentRef);
  scenesCollection.value = useCollection(scenesCollectionRef);
});
0

There are 0 best solutions below