Skip to content

Commit

Permalink
fix(templateRef): set ref on cached async component which wrapped in …
Browse files Browse the repository at this point in the history
…KeepAlive (#12290)

close #4999
close #5004
  • Loading branch information
edison1105 authored Nov 15, 2024
1 parent da7ad5e commit 983eb50
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 3 deletions.
66 changes: 66 additions & 0 deletions packages/runtime-core/__tests__/rendererTemplateRef.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {
KeepAlive,
defineAsyncComponent,
defineComponent,
h,
nextTick,
Expand Down Expand Up @@ -538,4 +540,68 @@ describe('api: template refs', () => {
'<div><div>[object Object],[object Object]</div><ul><li>2</li><li>3</li></ul></div>',
)
})

test('with async component which nested in KeepAlive', async () => {
const AsyncComp = defineAsyncComponent(
() =>
new Promise(resolve =>
setTimeout(() =>
resolve(
defineComponent({
setup(_, { expose }) {
expose({
name: 'AsyncComp',
})
return () => h('div')
},
}) as any,
),
),
),
)

const Comp = defineComponent({
setup(_, { expose }) {
expose({
name: 'Comp',
})
return () => h('div')
},
})

const toggle = ref(false)
const instanceRef = ref<any>(null)

const App = {
render: () => {
return h(KeepAlive, () =>
toggle.value
? h(AsyncComp, { ref: instanceRef })
: h(Comp, { ref: instanceRef }),
)
},
}

const root = nodeOps.createElement('div')
render(h(App), root)
expect(instanceRef.value.name).toBe('Comp')

// switch to async component
toggle.value = true
await nextTick()
expect(instanceRef.value).toBe(null)

await new Promise(r => setTimeout(r))
expect(instanceRef.value.name).toBe('AsyncComp')

// switch back to normal component
toggle.value = false
await nextTick()
expect(instanceRef.value.name).toBe('Comp')

// switch to async component again
toggle.value = true
await nextTick()
expect(instanceRef.value.name).toBe('AsyncComp')
})
})
16 changes: 13 additions & 3 deletions packages/runtime-core/src/rendererTemplateRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { isRef, toRaw } from '@vue/reactivity'
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
import type { SchedulerJob } from './scheduler'
import { queuePostRenderEffect } from './renderer'
import { getComponentPublicInstance } from './component'
import { type ComponentOptions, getComponentPublicInstance } from './component'
import { knownTemplateRefs } from './helpers/useTemplateRef'

/**
Expand All @@ -42,8 +42,18 @@ export function setRef(
}

if (isAsyncWrapper(vnode) && !isUnmount) {
// when mounting async components, nothing needs to be done,
// because the template ref is forwarded to inner component
// #4999 if an async component already resolved and cached by KeepAlive,
// we need to set the ref to inner component
if (
vnode.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE &&
(vnode.type as ComponentOptions).__asyncResolved &&
vnode.component!.subTree.component
) {
setRef(rawRef, oldRawRef, parentSuspense, vnode.component!.subTree)
}

// otherwise, nothing needs to be done because the template ref
// is forwarded to inner component
return
}

Expand Down

0 comments on commit 983eb50

Please sign in to comment.