})
)
- const fooRef = ref()
+ const fooRef = ref<any>(null)
const toggle = ref(true)
const root = nodeOps.createElement('div')
createApp({
expect(serializeInner(root)).toBe('resolved')
expect(fooRef.value.id).toBe('foo')
})
+
+ // #3188
+ test('the forwarded template ref should always exist when doing multi patching', async () => {
+ let resolve: (comp: Component) => void
+ const Foo = defineAsyncComponent(
+ () =>
+ new Promise(r => {
+ resolve = r as any
+ })
+ )
+
+ const fooRef = ref<any>(null)
+ const toggle = ref(true)
+ const updater = ref(0)
+
+ const root = nodeOps.createElement('div')
+ createApp({
+ render: () =>
+ toggle.value ? [h(Foo, { ref: fooRef }), updater.value] : null
+ }).mount(root)
+
+ expect(serializeInner(root)).toBe('<!---->0')
+ expect(fooRef.value).toBe(null)
+
+ resolve!({
+ data() {
+ return {
+ id: 'foo'
+ }
+ },
+ render: () => 'resolved'
+ })
+
+ await timeout()
+ expect(serializeInner(root)).toBe('resolved0')
+ expect(fooRef.value.id).toBe('foo')
+
+ updater.value++
+ await nextTick()
+ expect(serializeInner(root)).toBe('resolved1')
+ expect(fooRef.value.id).toBe('foo')
+
+ toggle.value = false
+ await nextTick()
+ expect(serializeInner(root)).toBe('<!---->')
+ expect(fooRef.value).toBe(null)
+ })
})
}
let value: ComponentPublicInstance | RendererNode | Record<string, any> | null
- if (!vnode || isAsyncWrapper(vnode)) {
+ if (!vnode) {
+ // means unmount
value = null
} else {
+ // when mounting async components, nothing needs to be done,
+ // because the template ref is forwarded to inner component
+ if (isAsyncWrapper(vnode)) return
if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
value = vnode.component!.exposed || vnode.component!.proxy
} else {