Skip to content

Commit

Permalink
fix(Suspense): handle switching away from kept-alive component before…
Browse files Browse the repository at this point in the history
… resolve

close #6416
using test from #6467
  • Loading branch information
yyx990803 committed Dec 12, 2023
1 parent 96aeb24 commit aa0c13f
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 4 deletions.
52 changes: 51 additions & 1 deletion packages/runtime-core/__tests__/components/Suspense.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import {
shallowRef,
SuspenseProps,
resolveDynamicComponent,
Fragment
Fragment,
KeepAlive
} from '@vue/runtime-test'
import { createApp, defineComponent } from 'vue'
import { type RawSlots } from 'packages/runtime-core/src/componentSlots'
Expand Down Expand Up @@ -1638,6 +1639,55 @@ describe('Suspense', () => {
expect(serializeInner(root)).toBe(expected)
})

// #6416
test('KeepAlive with Suspense', async () => {
const Async = defineAsyncComponent({
render() {
return h('div', 'async')
}
})
const Sync = {
render() {
return h('div', 'sync')
}
}
const components = [Async, Sync]
const viewRef = ref(0)
const root = nodeOps.createElement('div')
const App = {
render() {
return h(KeepAlive, null, {
default: () => {
return h(Suspense, null, {
default: h(components[viewRef.value]),
fallback: h('div', 'Loading-dynamic-components')
})
}
})
}
}
render(h(App), root)
expect(serializeInner(root)).toBe(`<div>Loading-dynamic-components</div>`)

viewRef.value = 1
await nextTick()
expect(serializeInner(root)).toBe(`<div>sync</div>`)

viewRef.value = 0
await nextTick()

expect(serializeInner(root)).toBe('<!---->')

await Promise.all(deps)
await nextTick()
// when async resolve,it should be <div>async</div>
expect(serializeInner(root)).toBe('<div>async</div>')

viewRef.value = 1
await nextTick() //TypeError: Cannot read properties of null (reading 'parentNode'),This has been fixed
expect(serializeInner(root)).toBe(`<div>sync</div>`)
})

describe('warnings', () => {
// base function to check if a combination of slots warns or not
function baseCheckWarn(
Expand Down
13 changes: 10 additions & 3 deletions packages/runtime-core/src/components/Suspense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ export interface SuspenseProps {

export const isSuspense = (type: any): boolean => type.__isSuspense

// incrementing unique id for every pending branch
let suspenseId = 0

// Suspense exposes a component-like API, and is treated like a component
// in the compiler, but internally it's a special built-in type that hooks
// directly into the renderer.
Expand Down Expand Up @@ -249,7 +252,8 @@ function patchSuspense(
}
} else {
// toggled before pending tree is resolved
suspense.pendingId++
// increment pending ID. this is used to invalidate async callbacks
suspense.pendingId = suspenseId++
if (isHydrating) {
// if toggled before hydration is finished, the current DOM tree is
// no longer valid. set it as the active branch so it will be unmounted
Expand All @@ -259,7 +263,6 @@ function patchSuspense(
} else {
unmount(pendingBranch, parentComponent, suspense)
}
// increment pending ID. this is used to invalidate async callbacks
// reset suspense state
suspense.deps = 0
// discard effects from pending branch
Expand Down Expand Up @@ -350,7 +353,11 @@ function patchSuspense(
triggerEvent(n2, 'onPending')
// mount pending branch in off-dom container
suspense.pendingBranch = newBranch
suspense.pendingId++
if (newBranch.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
suspense.pendingId = newBranch.component!.suspenseId!
} else {
suspense.pendingId = suspenseId++
}
patch(
null,
newBranch,
Expand Down

0 comments on commit aa0c13f

Please sign in to comment.