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:

thanksd
  • 54,176
  • 22
  • 157
  • 150
ATLChris
  • 3,198
  • 7
  • 39
  • 65
  • Can you show the related code? What's in your `v-if`? – thanksd Nov 06 '17 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. – ATLChris Nov 06 '17 at 21:53
  • @thanksd I have added the full code for both the parent and child components to the bottom of the original question. – ATLChris Nov 06 '17 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. – thanksd Nov 06 '17 at 22:13

1 Answers1

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.

thanksd
  • 54,176
  • 22
  • 157
  • 150
  • 2
    So the DevTools actually do trigger the computer properties? Does this cause some weird things while you debug? – 8bit Nov 06 '17 at 23:47
  • 3
    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). – thanksd Nov 07 '17 at 00:41
  • 2
    Thanks for the explanation about Vue Devtools and the tip about adjusting how I use computed properties. – ATLChris Nov 07 '17 at 01:12
  • According to [devtools.vuejs.org](https://devtools.vuejs.org/guide/faq.html#the-data-isn-t-updating-in-the-component-inspector) 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. – Skeletor May 31 '22 at 11:03