Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Props watch not working in Vue composition api

I have a state object being passed through as a prop from another component. I receive the state as expected. I then store the props in a toRef() for reactivity.

What I want to happen is when the data in state changes, I want to data in the browser to update. As it is right now, the data does get updated in the vue dev tools and I do see the state object change but the browser does not reflect the changes until I manually refresh the browser, which is an undesired result.

Why is this happening:

Component:

<template>
      <div class="show-products">
        <Product
            v-for="data in products"
            :key="data.id"
            :product="data"
        />
      </div>
 </template>

<script>
import {computed, onMounted, toRefs, watch } from "vue";
import {useStore} from "vuex";
    
    export default {
      components: {Product},
      props: {
        state: Object
      },
      setup (props) {
        const store = useStore();
        const products = computed(() => store.getters.getProducts);
    
        let stateRef = toRefs(props).state;
    
        onMounted(() => {
          store.dispatch('getProducts');
        });
    
        watch(stateRef, (currentState, prevState) => {
          if (currentState.length !== prevState.length) {
            store.dispatch('getProducts');
          }
        })
    
        return {
          Product,
          products,
        }
      }
    }
</script>
like image 613
CodeConnoisseur Avatar asked Oct 20 '25 04:10

CodeConnoisseur


1 Answers

Observing the array length requires a deep watcher, so make sure to set the deep flag:

watch(stateRef, (currentState, prevState) => {/*...*/}, { deep: true })

Also, the watcher callback only receives the data references, so if you're only mutating the original array (e.g., via push() or pop()), the currentState and prevState arguments will be the same reference, which would defeat the length check for differences.

A potential solution is to remove the length check, and always dispatch the getProducts action in the watcher:

// ProductList.vue
watch(stateRef, (currentState, prevState) => {
  store.dispatch('getProducts')
}, { deep: true })

Another solution that does not require a deep watcher is to assign a new array to state instead of using push()/pop() on the original array:

// Parent.vue
addState(newValue) {
  state.value = [ ...state.value, newValue ]
}

demo

like image 112
tony19 Avatar answered Oct 23 '25 13:10

tony19