)
})
- test.todo('new async dep after resolve should cause suspense to restart')
+ test('new async dep after resolve should cause suspense to restart', async () => {
+ const toggle = ref(false)
+
+ const ChildA = createAsyncComponent({
+ setup() {
+ return () => h('div', 'Child A')
+ }
+ })
+
+ const ChildB = createAsyncComponent({
+ setup() {
+ return () => h('div', 'Child B')
+ }
+ })
+
+ const Comp = {
+ setup() {
+ return () =>
+ h(Suspense, null, {
+ default: [h(ChildA), toggle.value ? h(ChildB) : null],
+ fallback: h('div', 'root fallback')
+ })
+ }
+ }
+
+ const root = nodeOps.createElement('div')
+ render(h(Comp), root)
+ expect(serializeInner(root)).toBe(`<div>root fallback</div>`)
+
+ await deps[0]
+ await nextTick()
+ expect(serializeInner(root)).toBe(`<!----><div>Child A</div><!----><!---->`)
+
+ toggle.value = true
+ await nextTick()
+ expect(serializeInner(root)).toBe(`<div>root fallback</div>`)
+
+ await deps[1]
+ await nextTick()
+ expect(serializeInner(root)).toBe(
+ `<!----><div>Child A</div><div>Child B</div><!---->`
+ )
+ })
test.todo('portal inside suspense')
})
parentComponent,
container,
hiddenContainer,
- anchor
+ anchor,
+ isSVG,
+ optimized
))
const { content, fallback } = normalizeSuspenseChildren(n2)
if (__DEV__) {
if (suspense.isResolved) {
throw new Error(
- `suspense.resolve() is called when it's already resolved`
+ `resolveSuspense() is called on an already resolved suspense boundary.`
)
}
if (suspense.isUnmounted) {
throw new Error(
- `suspense.resolve() is called when it's already unmounted`
+ `resolveSuspense() is called on an already unmounted suspense boundary.`
)
}
}
const {
+ vnode,
subTree,
fallbackTree,
effects,
- vnode,
parentComponent,
container
} = suspense
}
}
+ function restartSuspense(suspense: HostSuspsenseBoundary) {
+ suspense.isResolved = false
+ const {
+ vnode,
+ subTree,
+ fallbackTree,
+ parentComponent,
+ container,
+ hiddenContainer,
+ isSVG,
+ optimized
+ } = suspense
+
+ // move content tree back to the off-dom container
+ const anchor = getNextHostNode(subTree)
+ move(subTree as HostVNode, hiddenContainer, null)
+ // remount the fallback tree
+ patch(
+ null,
+ fallbackTree,
+ container,
+ anchor,
+ parentComponent,
+ null, // fallback tree will not have suspense context
+ isSVG,
+ optimized
+ )
+ const el = (vnode.el = (fallbackTree as HostVNode).el as HostNode)
+ // suspense as the root node of a component...
+ if (parentComponent && parentComponent.subTree === vnode) {
+ parentComponent.vnode.el = el
+ updateHOCHostEl(parentComponent, el)
+ }
+
+ // invoke @suspense event
+ const onSuspense = vnode.props && vnode.props.onSuspense
+ if (isFunction(onSuspense)) {
+ onSuspense()
+ }
+ }
+
function processComponent(
n1: HostVNode | null,
n2: HostVNode,
// TODO handle this properly
throw new Error('Async component without a suspense boundary!')
}
+
+ // parent suspense already resolved, need to re-suspense
+ // use queueJob so it's handled synchronously after patching the current
+ // suspense tree
if (parentSuspense.isResolved) {
- // TODO if parentSuspense is already resolved it needs to enter waiting
- // state again
+ queueJob(() => {
+ restartSuspense(parentSuspense)
+ })
}
+
parentSuspense.deps++
instance.asyncDep
.catch(err => {
)
}
})
+
// give it a placeholder
const placeholder = (instance.subTree = createVNode(Empty))
processEmptyNode(null, placeholder, container, anchor)
parentSuspense,
isSVG
)
- let current = instance.vnode
- current.el = nextTree.el
+ instance.vnode.el = nextTree.el
if (next === null) {
// self-triggered update. In case of HOC, update parent component
// vnode el. HOC is indicated by parent instance's subTree pointing
vnode: HostVNode
parent: SuspenseBoundary<HostNode, HostElement> | null
parentComponent: ComponentInternalInstance | null
+ isSVG: boolean
+ optimized: boolean
container: HostElement
hiddenContainer: HostElement
anchor: HostNode | null
parentComponent: ComponentInternalInstance | null,
container: HostElement,
hiddenContainer: HostElement,
- anchor: HostNode | null
+ anchor: HostNode | null,
+ isSVG: boolean,
+ optimized: boolean
): SuspenseBoundary<HostNode, HostElement> {
return {
vnode,
parent,
parentComponent,
+ isSVG,
+ optimized,
container,
hiddenContainer,
anchor,