Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(runtime-core): avoid number type transition child key duplicating with index key #5779

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
35 changes: 35 additions & 0 deletions packages/runtime-core/__tests__/components/BaseTransition.spec.ts
@@ -1,4 +1,5 @@
import { vi } from 'vitest'
import { Fragment, TransitionGroup, VNode, VNodeArrayChildren } from '@vue/runtime-dom'
import {
nodeOps,
render,
Expand Down Expand Up @@ -1195,3 +1196,37 @@ describe('BaseTransition', () => {
})
})
})


describe('TransitionGroup', () => {
// #5761: When child has v-if, v-if will result in keys of number type for branches
test('should avoid existing number type child keys duplicate with default index when inherit parent key in case of <template v-for>', () => {
// <TransitionGroup>
// <template v-for="(item, idx) of ['A', 'B']" :key="item">
// <div></div> // key: A0/B0
// <div :key=0></div> simulate v-if branch key. Keys should be A1/B1, otherwise duplicate A0/B0
// <div></div> // key: A2/B2
// </template>
// </TransitionGroup>

const transitionGroupVNode = h(TransitionGroup, {}, () => ['A', 'B'].map((item, idx ) => {
return h(Fragment, { key: item }, [
h('div', {}, '' ),
h('div', { key: 0 }, '' ), // simulate v-if branch key
h('div', {}, '' ),
] )
}))

render(
transitionGroupVNode,
nodeOps.createElement('div')
)

expect(((transitionGroupVNode.component?.subTree.children as VNodeArrayChildren)[0] as VNode).key).toBe('A0')
expect(((transitionGroupVNode.component?.subTree.children as VNodeArrayChildren)[1] as VNode).key).toBe('A1')
expect(((transitionGroupVNode.component?.subTree.children as VNodeArrayChildren)[2] as VNode).key).toBe('A2')
expect(((transitionGroupVNode.component?.subTree.children as VNodeArrayChildren)[3] as VNode).key).toBe('B0')
expect(((transitionGroupVNode.component?.subTree.children as VNodeArrayChildren)[4] as VNode).key).toBe('B1')
expect(((transitionGroupVNode.component?.subTree.children as VNodeArrayChildren)[5] as VNode).key).toBe('B2')
})
})
9 changes: 8 additions & 1 deletion packages/runtime-core/src/components/BaseTransition.ts
Expand Up @@ -494,10 +494,17 @@ export function getTransitionRawChildren(
for (let i = 0; i < children.length; i++) {
let child = children[i]
// #5360 inherit parent key in case of <template v-for>
// #5761: when child.key is a number, it would be potentially
// duplicated with index i. In this case, ignore whatever existing
// number type child key (e.g. v-if node could have dedicated key based on branchs).
// Make it completely based on index i in children list to avoid duplication
const key =
parentKey == null
? child.key
: String(parentKey) + String(child.key != null ? child.key : i)
: String(parentKey) +
String(
child.key != null && typeof child.key !== 'number' ? child.key : i
)
// handle fragment children case, e.g. v-for
if (child.type === Fragment) {
if (child.patchFlag & PatchFlags.KEYED_FRAGMENT) keyedFragmentCount++
Expand Down