Skip to content

Commit 983eb50

Browse files
authored
fix(templateRef): set ref on cached async component which wrapped in KeepAlive (#12290)
close #4999 close #5004
1 parent da7ad5e commit 983eb50

File tree

2 files changed

+79
-3
lines changed

2 files changed

+79
-3
lines changed

packages/runtime-core/__tests__/rendererTemplateRef.spec.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import {
2+
KeepAlive,
3+
defineAsyncComponent,
24
defineComponent,
35
h,
46
nextTick,
@@ -538,4 +540,68 @@ describe('api: template refs', () => {
538540
'<div><div>[object Object],[object Object]</div><ul><li>2</li><li>3</li></ul></div>',
539541
)
540542
})
543+
544+
test('with async component which nested in KeepAlive', async () => {
545+
const AsyncComp = defineAsyncComponent(
546+
() =>
547+
new Promise(resolve =>
548+
setTimeout(() =>
549+
resolve(
550+
defineComponent({
551+
setup(_, { expose }) {
552+
expose({
553+
name: 'AsyncComp',
554+
})
555+
return () => h('div')
556+
},
557+
}) as any,
558+
),
559+
),
560+
),
561+
)
562+
563+
const Comp = defineComponent({
564+
setup(_, { expose }) {
565+
expose({
566+
name: 'Comp',
567+
})
568+
return () => h('div')
569+
},
570+
})
571+
572+
const toggle = ref(false)
573+
const instanceRef = ref<any>(null)
574+
575+
const App = {
576+
render: () => {
577+
return h(KeepAlive, () =>
578+
toggle.value
579+
? h(AsyncComp, { ref: instanceRef })
580+
: h(Comp, { ref: instanceRef }),
581+
)
582+
},
583+
}
584+
585+
const root = nodeOps.createElement('div')
586+
render(h(App), root)
587+
expect(instanceRef.value.name).toBe('Comp')
588+
589+
// switch to async component
590+
toggle.value = true
591+
await nextTick()
592+
expect(instanceRef.value).toBe(null)
593+
594+
await new Promise(r => setTimeout(r))
595+
expect(instanceRef.value.name).toBe('AsyncComp')
596+
597+
// switch back to normal component
598+
toggle.value = false
599+
await nextTick()
600+
expect(instanceRef.value.name).toBe('Comp')
601+
602+
// switch to async component again
603+
toggle.value = true
604+
await nextTick()
605+
expect(instanceRef.value.name).toBe('AsyncComp')
606+
})
541607
})

packages/runtime-core/src/rendererTemplateRef.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { isRef, toRaw } from '@vue/reactivity'
1515
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
1616
import type { SchedulerJob } from './scheduler'
1717
import { queuePostRenderEffect } from './renderer'
18-
import { getComponentPublicInstance } from './component'
18+
import { type ComponentOptions, getComponentPublicInstance } from './component'
1919
import { knownTemplateRefs } from './helpers/useTemplateRef'
2020

2121
/**
@@ -42,8 +42,18 @@ export function setRef(
4242
}
4343

4444
if (isAsyncWrapper(vnode) && !isUnmount) {
45-
// when mounting async components, nothing needs to be done,
46-
// because the template ref is forwarded to inner component
45+
// #4999 if an async component already resolved and cached by KeepAlive,
46+
// we need to set the ref to inner component
47+
if (
48+
vnode.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE &&
49+
(vnode.type as ComponentOptions).__asyncResolved &&
50+
vnode.component!.subTree.component
51+
) {
52+
setRef(rawRef, oldRawRef, parentSuspense, vnode.component!.subTree)
53+
}
54+
55+
// otherwise, nothing needs to be done because the template ref
56+
// is forwarded to inner component
4757
return
4858
}
4959

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy