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

React 18 : Hydration failed because the initial UI does not match what was rendered on the server. #2570

Closed
rphlmr opened this issue Mar 31, 2022 · 31 comments

Comments

@rphlmr
Copy link
Contributor

rphlmr commented Mar 31, 2022

What version of Remix are you using?

1.3.4

Steps to Reproduce

Update https://github.com/remix-run/indie-stack/tree/main

  • react: 18.0.0
  • react-dom: 18.0.0

Update entry.client.tsx :

import { hydrateRoot } from "react-dom/client";
import { RemixBrowser } from "remix";

hydrateRoot(document, <RemixBrowser />);

Run dev with NODE_ENV=production

Expected Behavior

No error

Actual Behavior

White screen with errors :

Uncaught Error: Hydration failed because the initial UI does not match what was rendered on the server.
    at throwOnHydrationMismatch (:3000/build/entry.client-K3EESZRH.js:10325:17)
    at tryToClaimNextHydratableInstance (:3000/build/entry.client-K3EESZRH.js:10335:15)
    at updateHostComponent$1 (:3000/build/entry.client-K3EESZRH.js:14601:13)
    at beginWork (:3000/build/entry.client-K3EESZRH.js:15679:22)
    at HTMLUnknownElement.callCallback2 (:3000/build/entry.client-K3EESZRH.js:3453:22)
    at Object.invokeGuardedCallbackDev (:3000/build/entry.client-K3EESZRH.js:3478:24)
    at invokeGuardedCallback (:3000/build/entry.client-K3EESZRH.js:3512:39)
    at beginWork$1 (:3000/build/entry.client-K3EESZRH.js:18903:15)
    at performUnitOfWork (:3000/build/entry.client-K3EESZRH.js:18367:20)
    at workLoopSync (:3000/build/entry.client-K3EESZRH.js:18307:13)
Warning: An error occurred during hydration. The server HTML was replaced with client content in <#document>.
Uncaught DOMException: Failed to execute 'appendChild' on 'Node': Only one element on document allowed.
    at appendChildToContainer (http://192.168.1.17:3000/build/entry.client-K3EESZRH.js:7946:24)
    at insertOrAppendPlacementNodeIntoContainer (http://192.168.1.17:3000/build/entry.client-K3EESZRH.js:16695:15)
    at insertOrAppendPlacementNodeIntoContainer (http://192.168.1.17:3000/build/entry.client-K3EESZRH.js:16702:15)
    at insertOrAppendPlacementNodeIntoContainer (http://192.168.1.17:3000/build/entry.client-K3EESZRH.js:16702:15)
    at insertOrAppendPlacementNodeIntoContainer (http://192.168.1.17:3000/build/entry.client-K3EESZRH.js:16702:15)
    at insertOrAppendPlacementNodeIntoContainer (http://192.168.1.17:3000/build/entry.client-K3EESZRH.js:16702:15)
    at insertOrAppendPlacementNodeIntoContainer (http://192.168.1.17:3000/build/entry.client-K3EESZRH.js:16702:15)
    at insertOrAppendPlacementNodeIntoContainer (http://192.168.1.17:3000/build/entry.client-K3EESZRH.js:16702:15)
    at insertOrAppendPlacementNodeIntoContainer (http://192.168.1.17:3000/build/entry.client-K3EESZRH.js:16702:15)
    at insertOrAppendPlacementNodeIntoContainer (http://192.168.1.17:3000/build/entry.client-K3EESZRH.js:16702:15)```
@rphlmr
Copy link
Contributor Author

rphlmr commented Mar 31, 2022

No error when NODE_ENV=development

@rphlmr
Copy link
Contributor Author

rphlmr commented Mar 31, 2022

🤷‍♂️ sorry can't reproduce. I close (maybe a caching issue ?)

@rphlmr rphlmr closed this as completed Mar 31, 2022
@DAlperin
Copy link

DAlperin commented Apr 1, 2022

@rphlmr Could we re-open this? Multiple people have faced this issue in the discord. I think the problem lies in upgrading an existing app but I have no idea why that would cause problems.
I am fairly confident caching isn't the issue. The problem persists across multiple browsers and private browsing .

@rphlmr
Copy link
Contributor Author

rphlmr commented Apr 1, 2022

@DAlperin Yes I can ;)
I reproduce if I npm run dev after a cypress run.
But if i clean build folder and re run dev, no more problems 🤷‍♂️

@rphlmr rphlmr reopened this Apr 1, 2022
@DAlperin
Copy link

DAlperin commented Apr 1, 2022

@rphlmr I just tried rm -rf tmp .cache build && npm run dev and the problem persists. Any ideas?

Also I am not using cypress, so the problem must be related but different

@rphlmr
Copy link
Contributor Author

rphlmr commented Apr 1, 2022

@DAlperin I have a demo here but It works
I think I follow the react migration guide to adapt this.

If it can help, i'm on macos with node v16.14.2

@DAlperin
Copy link

DAlperin commented Apr 2, 2022

@rphlmr I published my code to a branch of my repo. If you get the chance, do you think you could look it over or try running it?
https://github.com/DAlperin/dov.dev-remix/tree/react-18-broken

@rphlmr
Copy link
Contributor Author

rphlmr commented Apr 4, 2022

@DAlperin It works (react-18-broken) on my machine ^^"
image

@DAlperin
Copy link

DAlperin commented Apr 4, 2022

@rphlmr figured it out! remix-themes seemed to be playing weirdly with the new hydration in some edge cases. (I think the useTheme hook was returning conflicted values on the client and the server). I'll open up a issue over there to track. Thank you for your help!

@Schular
Copy link

Schular commented Apr 6, 2022

In my case Apollo Client Devtools was causing the app to crash after upgrade to React v18, because it was injecting a script tag on client side.

Who have this problem could try running in incognito mode in order to exclude browser extensions.

@ostwilkens
Copy link

"Dark Reader" extension also triggers this error. Of course, disabling extensions isn't a solution. I can't ask visitors to disable all extensions to view my site.
@MichaelDeBoey could you reopen this issue?

@hrgui
Copy link

hrgui commented Apr 14, 2022

Something that I've been wondering about:

hydrateRoot(document, <RemixBrowser />);

I don't think it's a good idea to hydrate the entire document because for chrome extensions that modify the DOM (e.g. Apollo Client DevTools, LastPass, Dark Reader), they will add / change the DOM significantly by injecting CSS / JS.

@danestves
Copy link

I have the same problem with cypress ended up doing this workaround

// Dependencies
import { RemixBrowser } from "@remix-run/react";
import { hydrateRoot } from "react-dom/client";

if (process.env.NODE_ENV === "test") {
  require("react-dom").hydrate(<RemixBrowser />, document);
} else {
  hydrateRoot(document, <RemixBrowser />);
}

@CanRau
Copy link
Contributor

CanRau commented Apr 21, 2022

Happened to me due to Yoroi
And agree with @ostwilkens expecting user (and developers) to disable browser extensions is not a solution

@hrgui yea, though I love that Remix gives us the full control, maybe hydrating document.head & document.body instead might help? Well Yoroi is injecting before <head> not sure what others do, if they inject into <head> oder <body> same issue would happen I guess? 🤔

@hrgui
Copy link

hrgui commented Apr 22, 2022

@CanRau if we do hydrate document.body instead, we need to change how entry.server works, otherwise there will be a mismatch - because entry.server renders the entire document.

@CanRau
Copy link
Contributor

CanRau commented Apr 22, 2022

Yeah I imagined it wouldn't be thaat simple, just a quick thought^^

@hrgui
Copy link

hrgui commented Apr 23, 2022

With a non-remix app, I was able to get it to work after observing what next.js and hydrogen does in regards to streaming:

They both have a div element they are hydrating.

hrgui/nodejs-react-sc-demo@ac87234

  1. The server renders the entire document with a <div id="root"></div> wrapping the react app.
  2. The client only hydrates the the #root element, and renders <App />

I tried replicating the same in remix, but an issue arises - both <RemixServer /> and <RemixBrowser /> return the entire document. <RemixBrowser /> just needs to render the portion that has to be hydrated.

@DAlperin
Copy link

@rphlmr I think this issue should be reopened. I deployed my first production remix app and have gotten consistent reports from users who have any extension that mutated the DOM (many many of them) I'm going to revert to react 17 tomorrow, but this issue should be considered 'breaking'.

If you'd prefer, I can open another issue.

DAlperin added a commit to DAlperin/dov.dev-remix that referenced this issue Apr 24, 2022
@machour
Copy link
Collaborator

machour commented Apr 24, 2022

@DAlperin you can comment on this new issue: #2947
Clearly, asking users to disable their extension can't be the solution.

@jacob-ebey
Copy link
Member

Cross linking this facebook/react#22833

@rphlmr
Copy link
Contributor Author

rphlmr commented Apr 25, 2022

@rphlmr I think this issue should be reopened. I deployed my first production remix app and have gotten consistent reports from users who have any extension that mutated the DOM (many many of them) I'm going to revert to react 17 tomorrow, but this issue should be considered 'breaking'.

If you'd prefer, I can open another issue.

Sorry I'm unable to do that :/ (no reopen option)

@edwinvrgs
Copy link

I have the same problem with cypress ended up doing this workaround

// Dependencies
import { RemixBrowser } from "@remix-run/react";
import { hydrateRoot } from "react-dom/client";

if (process.env.NODE_ENV === "test") {
  require("react-dom").hydrate(<RemixBrowser />, document);
} else {
  hydrateRoot(document, <RemixBrowser />);
}

This worked for me!

@alirezabonab
Copy link
Contributor

Any update here?

@machour
Copy link
Collaborator

machour commented Jun 6, 2022

@alireza-bonab This issue is closed, #2947

@tanduong
Copy link

This issue still happens with react 18.2.0 as I init the project with the indie stack.

@dan-cooke
Copy link

dan-cooke commented Aug 21, 2022

I just went back to using hydrate from react-dom instead of the new hydrateRoot, the fix suggested by @danestves for cypress, only works if you are running cypress locally.

For running it against deployed staging environments etc, it will not work.

Awaiting a fix in the meantime

@jgentes
Copy link

jgentes commented Sep 25, 2022

Taking the lead from @dan-cooke this seems to work nicely (using React 18.2.0 with @emotion and not disabling browser plugins):

import { RemixBrowser } from '@remix-run/react'
import { hydrate } from 'react-dom'

hydrate(<RemixBrowser />, document)

@simmondsty
Copy link

This issue remains.

import { hydrate } from 'react-dom'

Is working for now but not ideal since it only allows you to make use of react v17 features.

@rphlmr
Copy link
Contributor Author

rphlmr commented Sep 27, 2022

I have a Remix stack following indie stack and my cypress runs fine (when it's not timed out 🤭) on GitHub action.

I just have to add this, somewhere in cypress/support :

(source)

Cypress.on("uncaught:exception", (err) => {
  // Cypress and React Hydrating the document don't get along
  // for some unknown reason. Hopefully, we figure out why eventually
  // so we can remove this.
  if (
    /hydrat/i.test(err.message) ||
    /Minified React error #418/.test(err.message) ||
    /Minified React error #423/.test(err.message)
  ) {
    return false;
  }
});

But, even with entry files like the indie stack, you'll still have an error in the console in prod (not breaking things but disgracious)

@diboune
Copy link

diboune commented Oct 17, 2022

any news on this? other than using the React 17 hydrate

@machour
Copy link
Collaborator

machour commented Oct 17, 2022

@diboune As you can see, this issue is closed... please open a new issue if you're facing a problem.

@remix-run remix-run locked as resolved and limited conversation to collaborators Oct 17, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests