Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rerender Async Component when it is root node of a Sync component before hydration crash the vue runtime. #6949

Closed
mmis1000 opened this issue Oct 25, 2022 · 2 comments · Fixed by #7290
Labels
has PR A pull request has already been submitted to solve the issue scope: ssr scope: suspense

Comments

@mmis1000
Copy link
Contributor

mmis1000 commented Oct 25, 2022

Vue version

3.2.41

Link to minimal reproduction

https://github.com/mmis1000/vue-ssr-hydration-bug-report

Steps to reproduce

  1. Create an SSR app structured like this in vdom
<!-- App.vue -->
<Suspense>
  <Child>
</Suspense>
<!-- Child.vue -->
<AsyncWrapper :value="context.value" />
<BadSyncComponent />
<!-- AsyncWrapper.vue -->
<Async />
<!-- Async.vue -->
<template>
  <div><div/>
</template>
<script setup>
await new Promise(r => r())
</script>
<!-- Sync.vue -->
<template>
  <div><div/>
</template>
<script setup>
context.value = 2
</script>

The Sync.vue is a bad component that tries to change the value from context before rendering, which is already tracked by its sibling component.

This would cause the AsyncWrapper to be re-rendered even it is not hydrated yet.

  1. Start the app and open it in browser

What is expected?

The app reports hydration mismatch but otherwise functions normally.

What is actually happening?

The whole app crashed with following error.

Uncaught (in promise) TypeError: can't access property "shapeFlag", vnode is null
    getNextHostNode runtime-core.esm-bundler.js:6229
    getNextHostNode runtime-core.esm-bundler.js:6230
    componentUpdateFn runtime-core.esm-bundler.js:5699
    run reactivity.esm-bundler.js:187
    update runtime-core.esm-bundler.js:5729
    updateComponent runtime-core.esm-bundler.js:5554
    processComponent runtime-core.esm-bundler.js:5487
    patch runtime-core.esm-bundler.js:5085
    patchKeyedChildren runtime-core.esm-bundler.js:5849
    patchChildren runtime-core.esm-bundler.js:5792
    processFragment runtime-core.esm-bundler.js:5472
    patch runtime-core.esm-bundler.js:5078
    componentUpdateFn runtime-core.esm-bundler.js:5695
    run reactivity.esm-bundler.js:187
    update runtime-core.esm-bundler.js:5729
    callWithErrorHandling runtime-core.esm-bundler.js:155
    flushJobs runtime-core.esm-bundler.js:388
    promise callback*queueFlush runtime-core.esm-bundler.js:280
    queueJob runtime-core.esm-bundler.js:274
    effect runtime-core.esm-bundler.js:5727
    triggerEffect reactivity.esm-bundler.js:396
    triggerEffects reactivity.esm-bundler.js:386
    trigger reactivity.esm-bundler.js:354
    set2 reactivity.esm-bundler.js:525
    setup SyncInner.vue:9
    callWithErrorHandling runtime-core.esm-bundler.js:155
    setupStatefulComponent runtime-core.esm-bundler.js:7204
    setupComponent runtime-core.esm-bundler.js:7159
    mountComponent runtime-core.esm-bundler.js:5508
    hydrateNode runtime-core.esm-bundler.js:4714
    hydrateChildren runtime-core.esm-bundler.js:4853
    hydrateFragment runtime-core.esm-bundler.js:4879
    hydrateNode runtime-core.esm-bundler.js:4694
    hydrateSubTree runtime-core.esm-bundler.js:5594
    componentUpdateFn runtime-core.esm-bundler.js:5608
    run reactivity.esm-bundler.js:187
    update runtime-core.esm-bundler.js:5729
    setupRenderEffect runtime-core.esm-bundler.js:5743
    mountComponent runtime-core.esm-bundler.js:5525
    hydrateNode runtime-core.esm-bundler.js:4714
    hydrateChildren runtime-core.esm-bundler.js:4853
    hydrateElement runtime-core.esm-bundler.js:4815
    hydrateNode runtime-core.esm-bundler.js:4705
    hydrateSuspense runtime-core.esm-bundler.js:1533
    hydrateNode runtime-core.esm-bundler.js:4756
    hydrateChildren runtime-core.esm-bundler.js:4853
    hydrateElement runtime-core.esm-bundler.js:4815
    hydrateNode runtime-core.esm-bundler.js:4705
    hydrateSubTree runtime-core.esm-bundler.js:5594
    componentUpdateFn runtime-core.esm-bundler.js:5608
    run reactivity.esm-bundler.js:187
    update runtime-core.esm-bundler.js:5729
    setupRenderEffect runtime-core.esm-bundler.js:5743
    mountComponent runtime-core.esm-bundler.js:5525
    hydrateNode runtime-core.esm-bundler.js:4714
    hydrate2 runtime-core.esm-bundler.js:4608
    mount runtime-core.esm-bundler.js:4437
    mount runtime-dom.esm-bundler.js:1593
     entry-client.ts:6

System Info

System:
    OS: Windows 10 10.0.22621
    CPU: (32) x64 AMD Ryzen 9 5950X 16-Core Processor
    Memory: 31.10 GB / 63.91 GB
  Binaries:
    Node: 16.13.2 - C:\Program Files\nodejs\node.EXE
    npm: 8.1.2 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Spartan (44.22621.674.0), Chromium (106.0.1370.52)
    Internet Explorer: 11.0.22621.1
  npmPackages:
    vue: ^3.2.31 => 3.2.41

Any additional comments?

The bug is caused by conflict between

if (!initialVNode.el) {
and
getNextHostNode(prevTree),

During hydration, the el exists but subtree is actually null.

And when you trigger update in this case.
The second line above calls

return getNextHostNode(vnode.component!.subTree)
to retrieve the subtree for the mount point, which in turns didn't work and crash the whole app.

This bug happened to me when I am using vee-validate in the nuxt3 and it does this

https://github.com/logaretm/vee-validate/blob/8ccfd2b2b542963d3d35cfe5f82490c94ec1635f/packages/vee-validate/src/useForm.ts#L526

in its setup. The fieldsByPath.value is a context object shared by its consumer. Which may already has been rendered in the vdom but not hydrated yet like report above.

@ml1nk
Copy link

ml1nk commented Nov 13, 2022

Related to #6095?

@mmis1000
Copy link
Contributor Author

mmis1000 commented Nov 13, 2022

Related to #6095?

Seems to be exactly the path that cause error.

Although the prompt way of fix this issue seems questionable to me. (I don't have enough knowledge about suspense to say it is right or wrong with confident)

Is it actually actually okay to ignore a partially loaded component when update the tree?

@haoqunjiang haoqunjiang added scope: suspense scope: ssr has PR A pull request has already been submitted to solve the issue labels Mar 22, 2023
@github-actions github-actions bot locked and limited conversation to collaborators Jan 12, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
has PR A pull request has already been submitted to solve the issue scope: ssr scope: suspense
Projects
None yet
3 participants