7

I am using VueJS 2.5.3 on a section (not SPA) of a blog backend that makes an API call to check for a featured image attached to the post.

If it finds one, it uses a child component to show the image. The problem is that the child component isn't rendering after the API call is successful and so neither is the image object passed to it.

As you can see in this GIF, the child component isn't rendering <!---->, I have a v-if on it to check if the image exists. However, if I click on the child component inside of Vue DevTools, the child component renders and shows the image as expected.

My question is why would a child component only render after clicking on it in Vue Devtools? Does Vue Devtools trigger some sort of an event when you click on a component?

Here is the child component:

<template>
    <div v-if="showImage" class="featured-image-container" :class="[ size ]">
        <img :src="processedSrc" alt="Featured Image">
    </div>
</template>

<script>
export default {
    props: {
        image: {
            type: Object
        },
        size: {
            type: String,
            required: true
        }
    },
    data () {
        return {
            showImage: false
        }
    },
    computed: {
        processedSrc: function () {
            if (this.image && typeof this.image === 'object') {
                this.showImage = true
                return this.image.sizes[this.size].file
            } else {
                this.showImage = false
            }
        }
    }
}
</script>

And here is a link to the code for the parent and child components:

4
  • Can you show the related code? What's in your v-if? Commented Nov 6, 2017 at 21:50
  • @thanksd Sure, the v-if is a simple bool I set if the API call returns a image. v-if="showImage" I can confirm this is set to true correctly when an image is found. Commented Nov 6, 2017 at 21:53
  • @thanksd I have added the full code for both the parent and child components to the bottom of the original question. Commented Nov 6, 2017 at 21:59
  • Please add the code for your PostFeaturedImage.vue component to the question instead of just providing a link. That's where the core issue lies, and if the link ever broke, it would be hard to tell what is causing the problem. Commented Nov 6, 2017 at 22:13

1 Answer 1

13

The issue is in your PostFeaturedImage.vue component. You are depending on a computed value processedSrc to set a data property showImage.

However, showImage is initially false and you are using it in the v-if directive on the root element. This means that Vue will not render that element or the <img> element inside of it.

Computed properties in Vue are lazy-loaded, meaning their functions are not called until they are referenced. Since the processedSrc computed property is only being referenced on the <img> element (and since that element is not being rendered) its method is not getting called, meaning the showImage property is never set to true.

However, when you inspect a component in Vue DevTools, it lists all of the computed properties, meaning that the method for the processedSrc computed is getting called, and the showImage property is being set in that case.


The easiest solution to your issue would be to use v-show instead of v-if, since elements inside a v-show will be hidden but still rendered even if the value is false.

However, I would almost never recommend setting a data property's value based on logic within a function for a computed property. It creates unintended, hard-to-debug side-effects which lead to issues like the one you're currently experiencing.

I would suggest making your showImage property a computed property as well, based on the logic currently determining its value in the processedSrc computed method. Then, you can determine whether or not to try to calculate the value of the processedSrc computed based on the value of showImage.

computed: {
  showImage: function() {
    return this.image && typeof this.image === 'object';
  },
  processedSrc: function () {
    if (this.showImage) {
      return this.image.sizes[this.size].file;
    }
  }
}

This way, it is much easier to see what is affecting what and your code will be easier to maintain.

Sign up to request clarification or add additional context in comments.

4 Comments

So the DevTools actually do trigger the computer properties? Does this cause some weird things while you debug?
Yep. And it sure does. The basic idea of a computed property is to calculate and return a value based on dependant Vue instance properties. So Vue assumes if you never try to access a computed property, there's no point in running the method to calculate the value. But, if you're bending the rules of what a computed property is meant to do (by setting other properties, or emitting events, or making api calls, etc.) you'll get some unexpected results if your computed property is never accessed (or if it is accessed when you're not expecting it to be, like when inspecting with Vue DevTools).
Thanks for the explanation about Vue Devtools and the tip about adjusting how I use computed properties.
According to devtools.vuejs.org Make sure your data is used somewhere in your templates. Vue uses a lazy reactivity system for performance reasons, so the devtools could read some component data but Vue might not trigger updates on it as you would expect. You can also click on the Force refresh button at the top of the devtools to do a manual refresh of the component data.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.