Skip to content

Commit

Permalink
fix(suspense): fix suspense regression for errored template component
Browse files Browse the repository at this point in the history
fix #3857
  • Loading branch information
yyx990803 committed May 31, 2021
1 parent c7efb96 commit 44996d1
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 8 deletions.
43 changes: 43 additions & 0 deletions packages/runtime-core/__tests__/components/Suspense.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,49 @@ describe('Suspense', () => {
expect(serializeInner(root)).toBe(`<div>oops</div>`)
})

// #3857
test('error handling w/ template optimization', async () => {
const Async = {
async setup() {
throw new Error('oops')
}
}

const Comp = {
template: `
<div v-if="errorMessage">{{ errorMessage }}</div>
<Suspense v-else>
<div>
<Async />
</div>
<template #fallback>
<div>fallback</div>
</template>
</Suspense>
`,
components: { Async },
setup() {
const errorMessage = ref<string | null>(null)
onErrorCaptured(err => {
errorMessage.value =
err instanceof Error
? err.message
: `A non-Error value thrown: ${err}`
return false
})
return { errorMessage }
}
}

const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(serializeInner(root)).toBe(`<div>fallback</div>`)

await Promise.all(deps)
await nextTick()
expect(serializeInner(root)).toBe(`<div>oops</div>`)
})

it('combined usage (nested async + nested suspense + multiple deps)', async () => {
const msg = ref('nested msg')
const calls: number[] = []
Expand Down
19 changes: 11 additions & 8 deletions packages/runtime-core/src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1073,16 +1073,19 @@ function baseCreateRenderer(
const newVNode = newChildren[i]
// Determine the container (parent element) for the patch.
const container =
// oldVNode may be an errored async setup() component inside Suspense
// which will not have a mounted element
oldVNode.el &&
// - In the case of a Fragment, we need to provide the actual parent
// of the Fragment itself so it can move its children.
oldVNode.type === Fragment ||
// - In the case of different nodes, there is going to be a replacement
// which also requires the correct parent container
!isSameVNodeType(oldVNode, newVNode) ||
// - In the case of a component, it could contain anything.
oldVNode.shapeFlag & ShapeFlags.COMPONENT ||
oldVNode.shapeFlag & ShapeFlags.TELEPORT
? hostParentNode(oldVNode.el!)!
(oldVNode.type === Fragment ||
// - In the case of different nodes, there is going to be a replacement
// which also requires the correct parent container
!isSameVNodeType(oldVNode, newVNode) ||
// - In the case of a component, it could contain anything.
oldVNode.shapeFlag & ShapeFlags.COMPONENT ||
oldVNode.shapeFlag & ShapeFlags.TELEPORT)
? hostParentNode(oldVNode.el)!
: // In other cases, the parent container is not actually used so we
// just pass the block element here to avoid a DOM parentNode call.
fallbackContainer
Expand Down

0 comments on commit 44996d1

Please sign in to comment.