]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(Suspense): emit initial fallback and pending events (#3965)
authorEduardo San Martin Morote <posva@users.noreply.github.com>
Mon, 21 Jun 2021 21:03:07 +0000 (23:03 +0200)
committerGitHub <noreply@github.com>
Mon, 21 Jun 2021 21:03:07 +0000 (17:03 -0400)
Fix #3964

packages/runtime-core/__tests__/components/Suspense.spec.ts
packages/runtime-core/src/components/Suspense.ts

index 4228bc8401c900069b5af97744b5de84e8e2a139..421bc0a8e963b00ef04fda3c032a413174952519 100644 (file)
@@ -69,6 +69,70 @@ describe('Suspense', () => {
     expect(serializeInner(root)).toBe(`<div>async</div>`)
   })
 
+  test('emits events', async () => {
+    const Async = defineAsyncComponent({
+      render() {
+        return h('div', 'async')
+      }
+    })
+
+    const onFallback = jest.fn()
+    const onResolve = jest.fn()
+    const onPending = jest.fn()
+
+    const show = ref(true)
+    const Comp = {
+      setup() {
+        return () =>
+          h(
+            Suspense,
+            {
+              onFallback,
+              onResolve,
+              onPending,
+              // force displaying the fallback right away
+              timeout: 0
+            },
+            {
+              default: () => (show.value ? h(Async) : null),
+              fallback: h('div', 'fallback')
+            }
+          )
+      }
+    }
+
+    const root = nodeOps.createElement('div')
+    render(h(Comp), root)
+    expect(onFallback).toHaveBeenCalledTimes(1)
+    expect(onPending).toHaveBeenCalledTimes(1)
+    expect(onResolve).toHaveBeenCalledTimes(0)
+
+    await Promise.all(deps)
+    await nextTick()
+    expect(onFallback).toHaveBeenCalledTimes(1)
+    expect(onPending).toHaveBeenCalledTimes(1)
+    expect(onResolve).toHaveBeenCalledTimes(1)
+
+    show.value = false
+    await nextTick()
+    expect(onFallback).toHaveBeenCalledTimes(1)
+    expect(onPending).toHaveBeenCalledTimes(2)
+    expect(onResolve).toHaveBeenCalledTimes(2)
+
+    deps.length = 0
+    show.value = true
+    await nextTick()
+    expect(onFallback).toHaveBeenCalledTimes(2)
+    expect(onPending).toHaveBeenCalledTimes(3)
+    expect(onResolve).toHaveBeenCalledTimes(2)
+
+    await Promise.all(deps)
+    await nextTick()
+    expect(onFallback).toHaveBeenCalledTimes(2)
+    expect(onPending).toHaveBeenCalledTimes(3)
+    expect(onResolve).toHaveBeenCalledTimes(3)
+  })
+
   test('nested async deps', async () => {
     const calls: string[] = []
 
index 57ba92c6fcdcf0b30557201610f85a35d921b6d4..85a2d1755bdd9a1a054b20019fb6a4ce68c3e766 100644 (file)
@@ -94,6 +94,16 @@ export const Suspense = ((__FEATURE_SUSPENSE__
   new (): { $props: VNodeProps & SuspenseProps }
 }
 
+function triggerEvent(
+  vnode: VNode,
+  name: 'onResolve' | 'onPending' | 'onFallback'
+) {
+  const eventListener = vnode.props && vnode.props[name]
+  if (isFunction(eventListener)) {
+    eventListener()
+  }
+}
+
 function mountSuspense(
   vnode: VNode,
   container: RendererElement,
@@ -137,6 +147,10 @@ function mountSuspense(
   // now check if we have encountered any async deps
   if (suspense.deps > 0) {
     // has async
+    // invoke @fallback event
+    triggerEvent(vnode, 'onPending')
+    triggerEvent(vnode, 'onFallback')
+
     // mount the fallback tree
     patch(
       null,
@@ -304,10 +318,7 @@ function patchSuspense(
     } else {
       // root node toggled
       // invoke @pending event
-      const onPending = n2.props && n2.props.onPending
-      if (isFunction(onPending)) {
-        onPending()
-      }
+      triggerEvent(n2, 'onPending')
       // mount pending branch in off-dom container
       suspense.pendingBranch = newBranch
       suspense.pendingId++
@@ -501,10 +512,7 @@ function createSuspenseBoundary(
       suspense.effects = []
 
       // invoke @resolve event
-      const onResolve = vnode.props && vnode.props.onResolve
-      if (isFunction(onResolve)) {
-        onResolve()
-      }
+      triggerEvent(vnode, 'onResolve')
     },
 
     fallback(fallbackVNode) {
@@ -521,10 +529,7 @@ function createSuspenseBoundary(
       } = suspense
 
       // invoke @fallback event
-      const onFallback = vnode.props && vnode.props.onFallback
-      if (isFunction(onFallback)) {
-        onFallback()
-      }
+      triggerEvent(vnode, 'onFallback')
 
       const anchor = next(activeBranch!)
       const mountFallback = () => {