DEV Community

Corey McCormick for Simple Software

Posted on • Edited on

The Surprising Results Of VueJS Conditional Rendering “v-if”

Photo by Daniel McCullough on Unsplash

Today was one of those days that everything seemed to go wrong while trying to create a simple VueJS component. The day that you question everything — am I a developer? What am I doing wrong?? How can this not be working??? Deep despair sets in as nothing on Google seems to find your exact problem. We have all been here. Stack Overflow has abandoned you and you must find an answer to the problem all by yourself. Hopefully, my troubles can save you an hour or two in the future.

First, a quick summary of the differences between v-if and v-show. v-if is used when one doesn’t want the DOM element to load onto the page unless the directive is set to true. Toggling the directive will cause the DOM element to be removed from the DOM and then re-inserted firing all associated VueJs life cycle events.

v-showis used to hide and display components within the DOM. The key differences come down to the method in which the elements are removed from the display. Instead of completely remove the DOM element; a CSS property is added display:none. It is important to note that the VueJS lifecycle events will not fire while using v-show.

v-show will always load the element to the DOM which results in a higher initial render but quicker display toggling. See more details in the documentation.

My situation began with a very simple VueJs Component very much like the following. JsFiddle (https://jsfiddle.net/simplycorey/7s20ra1e/11/) Notice that the alert “mounted” only occurs once even though the component should mount multiple times.

<div id="cache-demo">
  <cache-demo :component="component" v-if="component === 'first'"></cache-demo>
  <cache-demo :component="component" v-if="component === 'second'"></cache-demo>
<button @click="component = 'first'">
    First
  </button>
  <button @click="component = 'second'">
    Second
  </button>
</div>

I was attempting to retrieve data in the mounted function as I have done a thousand times before. All seemed normal when the page would load as my components would mount, retrieve their data and show up properly. The problem? Somehow the data was leaking between the two. This blew my mind at first because I had no logic to share the data between them. Checking the props verified that they were receiving the correct information. Continued debugging showed that the mounted, created, destroyed, and other lifecycle events were not firing.

So what gives? It boils down to VueJS attempting to be smart by using an internal cache system to speed up the rendering of the DOM. VueJS will automatically re-use the component and share its data between them. This can cause surprising and frustrating results if you do not know what is going on.

Adding a key attribute will alert VueJS that the components are indeed different and should not use this internal cache system. Instead, the components will fully load each time firing all of the VueJS lifecycle events.

Controlling Reusable Elements with key

Vue tries to render elements as efficiently as possible, often re-using them instead of rendering from scratch. Beyond helping make Vue very fast, this can have some useful advantages. Read more.

Updating our example to the following would provide the results I was looking for. You’ll notice that clicking first or second will always fire the mounted event. JsFiddle (https://jsfiddle.net/simplycorey/3uvde2p7/3/) Notice that “mounted” is always fired when switching the loaded components.

<div id="cache-demo">
  <cache-demo key="first" :component="component" v-if="component === 'first'"></cache-demo>
  <cache-demo key="second" :component="component" v-if="component === 'second'"></cache-demo>
<button @click="component = 'first'">
    First
  </button>
  <button @click="component = 'second'">
    Second
  </button>
</div>

Check out keep.sh if you enjoyed this article and learned something. keep.sh is a free server file transfer system that allows you to send a file with a single command. Try it from your MacBook terminal or any command line the supports curl now:

curl --upload-file ./yourLocalFile.txt https://keep.sh

https://keep.sh/3d1fd43a21/yourLocalFile.txt

I hope this example and explanation will save you some hours in the future!


Top comments (0)