Andres Felipe
Andres Felipe

Reputation: 4330

Component mounted twice

I have a simple component which is rendered by a click function, but it gets rendered twice, this is my code.

<SeeCompany
    :is="create"
    v-bind:companyId="companySelected"
    @closeChild="closeModule"
  />

when i clicked in the button i change the create value to 'SeeCompany' so it gets mounted, but it repeats the same component text twice on the screen.

<b-button block
    @click="create = 'SeeCompany'"
    class="m-sides"
    variant="outline-primary">
        Ver
</b-button>

here is the image:

enter image description here

EDIT: Here is the code in the mounted

export default class SeeCompany extends Vue {

    @Prop({ default: 0 }) private companyId !: number;

    constructor() {
        super();
    }

    private mounted() {
      console.log(this.companyId); --> This is consoling two ceros (0) and the passed value for instance = 1;
    }
}

Upvotes: 5

Views: 5979

Answers (1)

skirtle
skirtle

Reputation: 29092

There are two main uses for is.

  1. Working around limitations in in-DOM templates.
  2. Dynamic components.

For more information see https://v2.vuejs.org/v2/api/#is.

We can ignore the former case as it isn't relevant here.

Typically the second case looks a bit like this:

<component :is="childName" />

Here childName is a property of the component and determines the name of the child component to use. In your example you called it create.

The actual tag name used in the template doesn't really matter. It is common to use the dummy tag <component> for this purpose to avoid misleading future maintainers who may not immediately notice the :is. Whenever you see <component> you know you're in a dynamic component scenario.

When we talk about dynamic components it is important to appreciate exactly what we mean by 'dynamic' in this context. We are specifically talking about which component to use. We are not talking about determining whether or not to create the component in the first place.

In the code in the question the value of create is initially set to an empty string, ''. This is then passed to :is. If you inspect the DOM you'll find that this creates a comment node. While this does make some sense I am unclear if this is officially supported. I've not seen this behaviour documented anywhere and I suspect you may be getting lucky by falling down an internal code path that's intended for other things. It is not something I would be confident relying on in future versions of Vue.

The specific code of interest is:

<SeeCompany
  v-bind:is="create"
  v-bind:companyId="1"
/>
<SeeOther
  v-bind:is="create"
  v-bind:companyId="1"
/>

So if you inspect the DOM when create is '' you should find two comment nodes.

When create gets set to SeeCompany this is equivalent to:

<SeeCompany
  is="SeeCompany"
  v-bind:companyId="1"
/>
<SeeOther
  is="SeeCompany"
  v-bind:companyId="1"
/>

In turn this is equivalent to:

<SeeCompany
  v-bind:companyId="1"
/>
<SeeCompany
  v-bind:companyId="1"
/>

The result is the creation of two SeeCompany components. The original SeeOther tag is irrelevant here. This is why, as noted earlier, the convention exists to use a <component> tag to avoid being misleading.

Of course this isn't what you actually wanted the code to do. I'm unclear what the target behaviour is so I'm going to cover a few variations.

If you just want to show the components conditionally you'd use v-if instead:

<SeeCompany
  v-if="create"
  v-bind:companyId="1"
/>
<SeeOther
  v-if="create"
  v-bind:companyId="1"
/>

Usually you'd want create to be a proper boolean, false or true. So set the initial value to false with @click="create = true".

Of course this would show both SeeCompany and SeeOther at the same time. That may not be what you want either. Perhaps you only want to show one at once. For that you might do something like this:

<SeeCompany
  v-if="create === 'SeeCompany'"
  v-bind:companyId="1"
/>
<SeeOther
  v-if="create === 'SeeOther'"
  v-bind:companyId="1"
/>

Here the initial value of create should be a falsey value of some kind, possibly '', with @click="create = 'SeeCompany'" and @click="create = 'SeeOther'" on appropriate buttons.

If the props for the components are all the same, and especially if there are more than two components involved, you could try to simplify this using is:

<component
  :is="create"
  v-if="create"
  v-bind:companyId="1"
/>

This is shorter but arguably not as clear.

Upvotes: 3

Related Questions