vue 3 composition api, passing data and making it reactive

In my component I have a simple select menu with two options ("all", and "Investment"). The idea here is to get an array of data from a composable, and display on screen each row of this data. If I select "all" in the menu it displays all rows, if I select "Investment" it will filter the data and display only those with obj.link == "usa".

Once I fetch the data and bring it into my component, if I console.log the data, it works fine. If I console.log the data after i filter it, I get an empty array.

I have then tried to hard code the data in my component and test the filter function, and it works fine. So the error comes from how I am getting my data and how I try to use it. I have tried to use different hooks such as onMounted, but was unsuccessfull.

Here is a minimalistic sample of my code. Any suggestion or advice is more than welcome

The composable that fetches the data from my database looks like this:


import {ref} from 'vue'
import { projectFirestore } from '../firebase/config'
import { collection, getDocs } from "firebase/firestore";

const getActorDocs = () => {
    const actorDocs = []
    const error = ref(null)

const loadActors = async () => {
        try {
            const querySnapshot = await getDocs(collection(projectFirestore, "actors"));
            querySnapshot.docs.map(doc => {
                actorDocs.push(doc.data())
            }) 
        } catch (err) {
            error.value = err.message
            console.log(error.value)
        }
    }

    return { actorDocs, error, loadActors} 
}

export default getActorDocs

My component:

<template>
    <div class="col-2">
            <span class="lbl">MA</span>
            <select v-model="selectedMA" class="form-select"  >
                <option value="all">all</option>
                <option value="Investment">Investment</option>
            </select>
    </div>

    <p v-for="obj in actorListTest2" :key="obj" :value="obj"> {{obj}} </p>
<template/>


<script >
import {onMounted, onBeforeMount, ref} from 'vue'
import getActorDocs from '../../composables/getActorDocs'
export default {
setup(){

    const selectedMA = ref("Investment")
    const error = ref(null)
    const {actorDocs, loadActors} = getActorDocs()
    var actorListTest1 = actorDocs
    const actorListTest2 = ref([])
    
    loadActors() // loads actors array into actorDocs
    actorListTest2.value = actorListTest1


    console.log(actorListTest1) // <----- prints correctly (see image below)
    if(selectedMA.value === "all"){
      actorListTest2.value = actorListTest1
    }else{
      actorListTest2.value = actorListTest1.filter(obj => {
        return obj.link == selectedMA.value
      })
    }
    
    console.log(actorListTest2.value)  // <----- prints undefined !
    


    return { error, selectedMA, actorListTest2}
    }//setup
}
</script>

This is the output of console.log(actorListTest1):

enter image description here

Then this is the output of console.log(actorListTest2) after filtering :

enter image description here


This is a known problem with console.log, it shouldn't be used to debug object values in real time.

actorDocs is not reactive and won't work correctly with asynchronous operations in Vue. Side effects are supposed to be done in lifecycle hooks, e.g.: mounted.

In current state getActorDocs isn't ready to be used with composition API because it's limited to follow promise control flow in order to avoid this race condition:

onMounted(async () => {
    await loadActors();
    console.log(actorListTest2.value);
});

A correct way to avoid this is to make actorDocs reactive array or a ref:

const actorDocs = reactive([]);

In case there's a need to access filtered value in side effect, e.g. console.log, this is done in a watcher

const actorListTest2 = computed(() => actorDocs.filter(...));

watch(actorListTest2, v => console.log(v));   

onMounted(() => {
    loadActors();
});