Duplicate key for transition group fragment when child has v-if
Version
3.2.33
Reproduction link
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
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.
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.
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.
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.
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 đź‘Ť