Subscribe on changes!

Duplicate key for transition group fragment when child has v-if

avatar
Apr 20th 2022

Version

3.2.33

Reproduction link

sfc.vuejs.org/

Steps to reproduce

Have a <template v-for> inside a <TransitionGroup>, with multiple children and one of those children has v-if. For example:

<template>
  <TransitionGroup>
    <template v-for="(item, idx) of ['A', 'B']" :key="item">
      <div>{{ idx }}: {{ item }}1</div>
      <div v-if="idx < 1">{{ idx }}: {{ item }}2</div>
    </template>
  </TransitionGroup>
</template>

What is expected?

Here the keys should be A0, A1 and B0.

What is actually happening?

But instead we get A0, A0 and B0.


Without the v-if everything works correctly and we get A0, A1, B0 and B1.

In the reproduction I have used a component for the children, so the generated key is easily visible in the devtools.

Related commit: https://github.com/vuejs/core/commit/4311dddfa72b405b20f469f8f219ec3027972f55 Related issues: #4718, #5360, #5392

avatar
Apr 20th 2022

Not completely sure what you want to say, @coder-hxl . The visible output is fine, but the key attributes contain duplicates. You need to use the devtools to look at them.

avatar
Apr 21st 2022

should be a bug. this is because v-if with a default key. image

avatar
Apr 22nd 2022

I don’t know how the key calculation for the v-if works, but maybe it would be good to allow defining the child keys manually, instead of using the index here. Afaics the code can handle it in theory, but when I try to define the key on the parent template element as well as on each child, I get a compiler error: SyntaxError: <template v-for> key should be placed on the <template> tag.

avatar
Apr 22nd 2022

I guess we need to clarify expect behaviour first, is it supposed to be: ??

<TransitionGroup>
    <template v-for="(item, idx) of ['A', 'B']" :key="item">
      <Item>{{ idx }}: {{ item }}1</Item> // key: B0
      <Item>{{ idx }}: {{ item }}2</Item> // key: B1
      <Item v-if="idx < 1">{{ idx }}: {{ item }}3</Item>  // key: B2
      <template v-else><Item>no<Item></template>  // key: should here be B2 or B3 ?
      <Item>{{ idx }}: {{ item }}4</Item>   // key: should here be B3 or B4 ?
      <Item>{{ idx }}: {{ item }}5</Item>   // ....
    </template>
  </TransitionGroup>

My pull request 5779 results in following result:

<TransitionGroup>
    <template v-for="(item, idx) of ['A', 'B']" :key="item">
      <Item>{{ idx }}: {{ item }}1</Item> // key: B0
      <Item>{{ idx }}: {{ item }}2</Item> // key: B1
      <Item v-if="idx < 1">{{ idx }}: {{ item }}3</Item>  // key: B2 (if this branch is valid)
      <template v-else><Item>no<Item></template>  // key:  B2
      <Item>{{ idx }}: {{ item }}4</Item>   // key: B3
      <Item>{{ idx }}: {{ item }}5</Item>   // key: B4
    </template>
  </TransitionGroup>

It completely re-calculates keys based on index position in the list and ignores number type keys from v-if node.

avatar
Apr 22nd 2022

Off the top of my head I would say your solution sounds perfectly reasonable. I expect, however, that the user’s expectation might be different depending on the situation. That’s why I suggested allowing explicit keys for all raw children.

avatar
Apr 26th 2022

children component with v-if can be seted explicit key no warn

avatar
Apr 26th 2022

Ah, thank you for the tip, @iwusong. Setting a key on the v-if items is a good workaround.

avatar
Apr 26th 2022

explicit key on child v-if node is nice workaround, just need to be aware that a custom key of "number type" on v-if could still be duplicated with other default index key because the fundamental issue is caused by "number type keys on v-if node".

So using types other than number should be good enough đź‘Ť