Search code examples
javascriptvue.jsvuejs3pinia

Can't access specific object value of array of objects in Vue 3 deep watcher


I can't access the value of a specific object prop in an array through a deep watcher in Vue3.

I don't know if there's a problem with proxy arrays that are holding proxy objects.

I have the following data structure from a pinia store:

rule: [
  {
    key: "root",
    data: "root1 data",
    label: "",
    selectListItems: [
      {
        label: "root1",
        data: "root1",
      },{
        data: "root2",
        label: "root2",
      }
    ]
  }, 
  {
     key: "leaf1",
     data: "leaf1 data",
     label: "",
     selectListItems: [
       {
         data: "leaf1_1",
         label: "leaf1_1",
       },{
         data: "leaf1_2",
         label: "leaf1_2",
       }
     ]
  }
]

and later in my Component.vue I filled a list like this and defined a watcher

<script setup>
const root = store.getRule..find((e) => e.hasOwnProperty('key') && e.key === 'root')
const leaf1= store.getRule..find((e) => e.hasOwnProperty('key') && e.key === 'leaf1')
const listCollection = ref([])

listCollection.value = [{...root},{...leaf1}]

watch(listCollection , (val) => {
...
 console.log(val[0].data)
//expected output: root1 data

 console.log(val[1].data)
//expected output:leaf1 data

// but it both throws: Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'data')

}}, { deep: true })
</script>

<template>
  <div v-for="(item, index) in listCollection">
    <select-list v-model="item.data" :items="item.selectListItems">
       in here is some logic that binds the selected data from selectLstItems to the v-model on click, it works as intened
    </select-list>
  </div>
</template>

Error message in watch by console.log

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'data')


Solution

  • If val[0] does not exist, accessing undefined.data will result in exactly the same type error.

    watch(listCollection, (val) => {
      console.log(val) // will always have a value
    
      // check if what you want to print exists
      if (val[0] && val[0].data) {
        console.log(val[0].data);
      }
    
      // or in another way (if val is always guaranteed to be an array, then you only need to check if the specific element exists)
      console.log(val[1]?.data);
    
      console.log(val[1]?.data ?? 'val[1] not object');
    }, { deep: true });