Guide on incorporating Firebase "onSnapshot" listener values to update a Vue "ref" variable

I have implemented a Vue composable feature that utilizes a Firebase onSnapshot listener to track data changes.

The goal is to update a ref called documentsArray whenever there is a change detected by the listener.

Interestingly, when I log the contents of documentsArray from within the onSnapshot function, it appears to be functioning correctly as it holds an array of values. However, the issue arises when I try to log the same outside the onSnapshot function – the array turns out to be empty.

This discrepancy raises the question: Why does the array only contain values inside the onSnapshot function but not outside?

Ideally, the console.log statement outside the onSnapshot function should also display the array's values.

Below is a snippet of my TypeScript Vue composable:

import { ref, Ref, watchEffect } from "vue";
import { projectFirestore } from "@/firebase/config";
import {
  collection,
  query,
  orderBy,
  onSnapshot,
  DocumentData,
  Timestamp,
} from "firebase/firestore";

interface Document {
  id: string;
  message: string;
  name: string;
  createdAt: Timestamp;
}

const getCollection = (
  collectionString: string
): {
  documentsArray: Ref<Document[] | null>;
  error: Ref<string | null>;
} => {
  const documentsArray = ref<Document[] | null>(null);
  const error = ref<string | null>(null);
  const colRef = collection(projectFirestore, collectionString);
  const colRefOrdered = query(colRef, orderBy("createdAt"));

  const unsubscribe = onSnapshot(
    colRefOrdered,
    (snap) => {
      const results: Document[] = [];
      snap.docs.forEach((doc: DocumentData) => {
        doc.data().createdAt && //Ensures the server timestamp has been added
          results.push({
            ...doc.data(),
            id: doc.id,
          });
      });
      documentsArray.value = results;
      console.log("documentsArray.value inside snapshot", documentsArray.value); //<-- This works. It prints an array of documents.
      error.value = null;
    },
    (err) => {
      console.log(err.message);
      documentsArray.value = null;
      error.value = "could not fetch data";
    }
  );
  watchEffect((onInvalidate) => {
    onInvalidate(() => {
      unsubscribe();
    });
  });
  console.log("documentsArray.value outside snapshot", documentsArray.value); //<-- This does not work. It prints an empty array.
  return { documentsArray, error };
};

export default getCollection;

Answer №1

When working with the onSnapshot callback, it's important to remember that the code inside it runs asynchronously. To observe changes outside of this function, a watcher must be created to monitor those changes:

import { ref, Ref, watchEffect, watch } from "vue";

....
const unsubscribe = onSnapshot(
colRefOrdered,
(snap) => {
...
);

watch(documentsArray, (newVal, oldVal) {
console.log("documentsArray.value outside snapshot", documentsArray.value);
}, { deep: true })

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Created computed getter and setter for Vue models (also known as "props") using the syntax of Vue Class Component

After exploring the limitations of v-model in Vue 2.x in this Stack Overflow thread, I discovered a way to connect parent and child components using v-model. The solution proposed is as follows: --- ParentTemplate: <Child v-model="formData"> ...

Using Angular to create a dynamic form with looping inputs that reactively responds to user

I need to implement reactive form validation for a form that has dynamic inputs created through looping data: This is what my form builder setup would be like : constructor(private formBuilder: FormBuilder) { this.userForm = this.formBuilder.group({ ...

Discovering how to display the hidden messages section in the absence of any ongoing chats with the help of angular2

I currently have 2 tabs set up on my page. The active messages tab is functioning perfectly without any issues. However, I am encountering an error with the closed messages tab. If there are no messages in this tab, the system displays an error message w ...

Tips for defining the type of a parameter in a Vue Component

Is it possible to define a type 'VueComponent' for a component parameter in TypeScript? function callback(component: VueComponent???){ // some code } ...

'This' loses its value within a function once the decorator is applied

Scenario: Exploring the creation of a decorator to implement an interceptor for formatting output. Issue at Hand: Encountering 'this' becoming undefined post application of the decorator. // custom decorator function UseAfter(this: any, fn: (.. ...

Extracting information from VueJS component

Can you help me out with a question? I am currently learning VueJS and I've built a simple component: Vue.component('blog-post', { props: ['title'], template: '<h3>{{ title }}</h3>' }) I have also pas ...

Nuxt - Component not navigating pages properly when using router.push

index.vue - <template> <div> <Header /> <div class="container"> <SearchForm /> </div> </div> </template> <script> const Cookie = process.client ? require('js-cookie&a ...

Is it possible to use v-if when dealing with data that is loaded after computation

I am facing a challenge where I need to hide a component based on data that is loaded in the mounted lifecycle hook. However, it seems that using v-if with computed properties only is causing an issue because the data is not ready yet (since computed is ca ...

Retrieving information from a child component in Vue.js

I've been grappling with retrieving data from a child component using Vue.js. In my parent component, I need to display this data which is selectable in a Bootstrap table within the child component. Here's what I've done so far: Parent compo ...

How can we design a Protobuf structure for a map that contains an array of objects as the value?

I need help with encoding a map in a protobuf format. Here is an example of the map: const newVisMap = new Map<number, IOutput[]>(); The map contains an array of objects that share a common interface, as shown below (with one optional property): int ...

Using Angular and Typescript to Submit a Post Request

I am working on a basic Angular and Typescript page that consists of just one button and one text field. My goal is to send a POST request to a specific link containing the input from the text field. Here is my button HTML: <a class="button-size"> ...

Angular: Issue with FormArrays not iterating properly

Apologies for the lengthy post, but I needed to provide a detailed explanation of the issue I am facing. In my form, there is a control that contains an array of JSON data. I have created a reactive form based on this structure. Below are the JSON data an ...

Searching for two variables in an API using TypeScript pipes

I'm stuck and can't seem to figure out how to pass 2 variables using the approach I have, which involves some rxjs. The issue lies with my search functionality for a navigation app where users input 'from' and 'to' locations i ...

Angular - Tips for effectively positioning a fixed navbar while scrolling to sections sharing the same ID

In my Angular project, I've set up different sections on a page, each with its own unique ID. There's also a fixed-position navbar for easy navigation between these sections. My main objective is to ensure that when a user clicks on a menu item ( ...

How to refresh a specific component or page in Angular without causing the entire page to reload

Is there a way to make the selected file visible without having to reload the entire page? I want to find a cleaner method for displaying the uploaded document. public onFileSelected(event): void { console.log(this.fileId) const file = event.targe ...

Creating a dynamic URL for route model binding using Axios

I need assistance in creating a Vue Axios URL using slug and id, but I'm facing difficulties. Could someone please lend me a hand? axios.get('http://localhost/BUproject/postsByUser/' + slug / +id) .then(response => { this.po ...

Struggling with implementing Angular and TypeScript in this particular method

I'm dealing with a code snippet that looks like this: myMethod(data: any, layerId: string, dataSubstrings): void { someObject.on('click', function(e) { this.api.getSomething(a).subscribe((result: any) => { // ERROR CALL 1. It ...

The click event is malfunctioning when attempting to submit the form

My issue is that when I click the submit button, the form object is getting reset before being added to an array. I want to first add the form object to the array and then reset its values upon submission. <template> <div class="home"& ...

How can I modify my Axios Post request to receive a 201 status code in order to successfully save the data?

I am facing an issue when attempting to make a POST request locally with Axios on my NodeJS front-end app to my .NET core local server. The server returns a 204 status code and the axios request returns a pending promise. How can I change this to achieve a ...

Is there a way to integrate TypeScript into the CDN version of Vue?

For specific areas of my project, I am utilizing the Vue CDN. I would like to incorporate Typescript support for these sections as well. However, our technical stack limitations prevent us from using Vue CLI. Is there a method to import Vue.js into a bas ...