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

Bug: lazy + hydrateRoot + sibling = mismatch #26181

Closed
spudly opened this issue Feb 16, 2023 · 9 comments
Closed

Bug: lazy + hydrateRoot + sibling = mismatch #26181

spudly opened this issue Feb 16, 2023 · 9 comments

Comments

@spudly
Copy link
Contributor

spudly commented Feb 16, 2023

I'm trying to use React' 18's new APIs to enable server-side suspense, but I'm getting hydration mismatch errors when I try to hydrate components that are wrapped in both lazy() and <Suspense />.

React version: 18.2.0

[EDIT] See updated description / repro below

## Steps To Reproduce
  1. wrap a react component with lazy and Suspense
  2. server-side render it using renderToPipeableStream
  3. try to hydrate it using hydrateRoot
  4. look in dev console - notice errors

Link to code example: https://codesandbox.io/p/sandbox/dreamy-tristan-8ldu3o

The current behavior

Hydration fails with the following errors:

Warning: Expected server HTML to contain a matching <body> in <html>.
    at body
    at html
    at App
printWarning @ react-dom.development.js:73
react-dom.development.js:12517 
        
Uncaught Error: Hydration failed because the initial UI does not match what was rendered on the server.
    at throwOnHydrationMismatch (react-dom.development.js:12517:11)
    at tryToClaimNextHydratableInstance (react-dom.development.js:12545:9)
    at updateHostComponent (react-dom.development.js:19912:7)
    at beginWork (react-dom.development.js:21628:16)
    at HTMLUnknownElement.callCallback (react-dom.development.js:4151:16)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:4200:18)
    at invokeGuardedCallback (react-dom.development.js:4264:33)
    at beginWork$1 (react-dom.development.js:27461:9)
    at performUnitOfWork (react-dom.development.js:26567:14)
    at workLoopSync (react-dom.development.js:26476:7)
react-dom.development.js:73 
        
Warning: An error occurred during hydration. The server HTML was replaced with client content in <#document>.
printWarning @ react-dom.development.js:73
client.js:5 
        
onRecoverableError Error: Hydration failed because the initial UI does not match what was rendered on the server.
    at throwOnHydrationMismatch (react-dom.development.js:12517:11)
    at tryToClaimNextHydratableInstance (react-dom.development.js:12545:9)
    at updateHostComponent (react-dom.development.js:19912:7)
    at beginWork (react-dom.development.js:21628:16)
    at beginWork$1 (react-dom.development.js:27436:16)
    at performUnitOfWork (react-dom.development.js:26567:14)
    at workLoopSync (react-dom.development.js:26476:7)
    at renderRootSync (react-dom.development.js:26444:9)
    at performConcurrentWorkOnRoot (react-dom.development.js:25748:76)
    at workLoop (react.development.js:2653:36)
onRecoverableError @ client.js:5
client.js:5 
        
onRecoverableError Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
    at updateHostRoot (react-dom.development.js:19859:59)
    at beginWork (react-dom.development.js:21625:16)
    at beginWork$1 (react-dom.development.js:27436:16)
    at performUnitOfWork (react-dom.development.js:26567:14)
    at workLoopSync (react-dom.development.js:26476:7)
    at renderRootSync (react-dom.development.js:26444:9)
    at recoverFromConcurrentError (react-dom.development.js:25860:22)
    at performConcurrentWorkOnRoot (react-dom.development.js:25760:24)
    at workLoop (react.development.js:2653:36)
    at flushWork (react.development.js:2626:16)

The expected behavior

Successful hydration / no errors

@spudly spudly added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Feb 16, 2023
@eps1lon
Copy link
Collaborator

eps1lon commented Feb 16, 2023

Where can I see server and client console output in the repro?

I think you need to bundle the React packages instead of loading them via CDN. Have you tried removing them and then add import * as ReactDOM from 'react-dom/client' to client.js?

@eps1lon
Copy link
Collaborator

eps1lon commented Feb 16, 2023

I think this is caused by a missing head tag. The browser will always insert that element but then React tries to hydrate a body into the head from the user agent. Once you've added a head tag I get no more hydration mismatch: https://codesandbox.io/p/sandbox/react-lazy-suspense-hydration-mismatch-forked-cwkpd8?file=%2Fsrc%2FApp.js&selection=%5B%7B%22endColumn%22%3A1%2C%22endLineNumber%22%3A16%2C%22startColumn%22%3A1%2C%22startLineNumber%22%3A15%7D%5D

Can you confirm this works for you?

If that was indeed the cause, we should warn about a missing head inside html.

@eps1lon eps1lon added Component: Server Rendering and removed Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug labels Feb 16, 2023
@spudly
Copy link
Contributor Author

spudly commented Feb 16, 2023

Yes, I see that fixes the issue in this case. This was an attempt to reproduce a bug I have in a larger, private repo. I'll keep tweaking it and see if I can create a better reproduction.

@spudly
Copy link
Contributor Author

spudly commented Mar 2, 2023

Just a quick update to let you know this issue is not dead.

I'm think my problem is related to #24384 and the associated fix, #24404.

This has been incredibly difficult to debug. I'm still working on creating a minimal repro.

@spudly
Copy link
Contributor Author

spudly commented Mar 2, 2023

OK, here we go. I think I've nailed the bug down this time and have a minimal repro that shows the issue, which is that lazy components with siblings that are NOT wrapped in suspense boundaries result in a hydration mismatch error.

Repro: https://codepen.io/spudly_1470322188/pen/ZEMKEgY

Steps To Reproduce

  1. open the link above
  2. open the developer console
  3. observe hydration errors in console

Workarounds

As best as I can tell, when you encounter this issue, you have 3 options:

  1. Wrap the suspended component AND the sibling with a suspense boundary (note: this won't work in the sandbox because it generates different html on the backend)
  2. Stop using react lazy, so suspense is no longer involved. In the case of this repro, this means that in you change it to render instead of
  3. Remove the sibling element

Both the 1st and 2nd options change the render priority of the components in undesirable ways. The 1st changes high priority components to low priority, and the second changes low priority components to high priority. The third option is just not an option in real-world scenarios.

@spudly spudly changed the title Bug: lazy + Suspense + hydrateRoot = mismatch Bug: lazy + hydrateRoot + sibling = mismatch Mar 2, 2023
@spudly
Copy link
Contributor Author

spudly commented Mar 21, 2023

@eps1lon , have a chance to review the updated repro?

@spudly
Copy link
Contributor Author

spudly commented Jun 9, 2023

anybody?

Copy link

github-actions bot commented Apr 9, 2024

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

@github-actions github-actions bot added the Resolution: Stale Automatically closed due to inactivity label Apr 9, 2024
Copy link

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!

@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Apr 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants