diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts
index efc0507d81e..ed1e228c4cf 100644
--- a/packages/runtime-core/__tests__/hydration.spec.ts
+++ b/packages/runtime-core/__tests__/hydration.spec.ts
@@ -688,6 +688,54 @@ describe('SSR hydration', () => {
expect(container.innerHTML).toBe(`1`)
})
+ // #6638
+ test('Suspense + async component', async () => {
+ let isSuspenseResolved = false
+ let isSuspenseResolvedInChild: any
+ const AsyncChild = defineAsyncComponent(() =>
+ Promise.resolve(
+ defineComponent({
+ setup() {
+ isSuspenseResolvedInChild = isSuspenseResolved
+ const count = ref(0)
+ return () =>
+ h(
+ 'span',
+ {
+ onClick: () => {
+ count.value++
+ },
+ },
+ count.value,
+ )
+ },
+ }),
+ ),
+ )
+ const { vnode, container } = mountWithHydration('0', () =>
+ h(
+ Suspense,
+ {
+ onResolve() {
+ isSuspenseResolved = true
+ },
+ },
+ () => h(AsyncChild),
+ ),
+ )
+ expect(vnode.el).toBe(container.firstChild)
+ // wait for hydration to finish
+ await new Promise(r => setTimeout(r))
+
+ expect(isSuspenseResolvedInChild).toBe(false)
+ expect(isSuspenseResolved).toBe(true)
+
+ // assert interaction
+ triggerEvent('click', container.querySelector('span')!)
+ await nextTick()
+ expect(container.innerHTML).toBe(`1`)
+ })
+
test('Suspense (full integration)', async () => {
const mountedCalls: number[] = []
const asyncDeps: Promise[] = []
diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts
index 4452407c2a8..ddc46c70049 100644
--- a/packages/runtime-core/src/renderer.ts
+++ b/packages/runtime-core/src/renderer.ts
@@ -1276,7 +1276,7 @@ function baseCreateRenderer(
const componentUpdateFn = () => {
if (!instance.isMounted) {
let vnodeHook: VNodeHook | null | undefined
- const { el, props } = initialVNode
+ const { el, props, type } = initialVNode
const { bm, m, parent } = instance
const isAsyncWrapperVNode = isAsyncWrapper(initialVNode)
@@ -1325,8 +1325,11 @@ function baseCreateRenderer(
}
}
- if (isAsyncWrapperVNode) {
- ;(initialVNode.type as ComponentOptions).__asyncLoader!().then(
+ if (
+ isAsyncWrapperVNode &&
+ !(type as ComponentOptions).__asyncResolved
+ ) {
+ ;(type as ComponentOptions).__asyncLoader!().then(
// note: we are moving the render call into an async callback,
// which means it won't track dependencies - but it's ok because
// a server-rendered async wrapper is already in resolved state