简体   繁体   中英

Computed property in Vue is not triggering a watch

According to this post , it shouldn't be a problem to watch a computed property. And yet my code isn't working.

<template>
    <div v-if="product" class="section">
        <form>
            <div class="control"><input type="text" class="input" v-model="title"></div>
            <div class="control"><input type="text" class="input" v-model="description"></div>
        </form>
    </div>
</template>

<script>
export default {
    data() {
        return {
            title: null,
            description: null
        }
    },
    computed: {
        product() {
            // const payload = { collection: 'products', id: this.$route.params.productId }
            // return this.$store.getters.objectFromId(payload)
            console.log('working')
            return { title: 'Awesome Title', description: 'Awesome Description' }
        }
    },
    watch: {
        product() {
            this.title = this.product.title,
            this.description = this.product.description
        }
    }
}
</script>

I'm expecting the watch to trigger when product is returned, but it doesn't.

I could set the properties in the computed property like so:

computed: {
    product() {
        const payload = { collection: 'products', id: this.$route.params.productId }
        const product = this.$store.getters.objectFromId(payload)
        this.title = product.title
        this.description = product.description
        return product
    }
}

But then the compiler gives me a warning: error: Unexpected side effect in "product" computed property

Accordingly to OP's comments, his intention is to get and load some initial data. The common way to achieve this behavior is to place it inside created or mounted vuejs lifecycle hooks.

<template>
    <div v-if="product" class="section">
        <form>
            <div class="control"><input type="text" class="input" v-model="title"></div>
            <div class="control"><input type="text" class="input" v-model="description"></div>
        </form>
    </div>
</template>

<script>
export default {
    data() {
        return {
            title: '',
            description: ''
        }
    },

    created() {
        this.getInitialData();
        this.foo();

        console.log("created!");
    },

    methods: {
        getInitialData: function(){
            const payload = {
                collection: 'products', 
                id: this.$route.params.productId 
            };
            var product = this.$store.getters.objectFromId(payload);
            this.title = product.title;
            this.description = product.description;
        },
        foo: function(){// ...}
    },
}
</script>

Your structure is a bit all over the place. product is a computed, so it runs whenever it's source values change. (You have no control over when it runs.) It shouldn't have side effects (assignments this.description , this.title ), or trigger network requests.

The code in product is fetching your source data. This belongs in methods , linked explicitly to a user action or a lifecyle event.

Why do you need to copy your data ( this.description = product.description in watch:product )? Vue works best when you have your data (your app state) outside Vue, in a global variable say. Then your Vue components just transparently reflect whatever the app state is at a given moment.

Hope this helps.

Try the following:

watch: {
    product: {
        immediate: true,
        handler(value) { 
            updateCode();
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM