Open thomasjm opened 4 years ago
👍 will take a look
you have a mistake in SSR code - double closed div
- fullHtml = fullHtml.replace(`<div id="app">`, `\n<div id="app">${html}</div>`)
+ fullHtml = fullHtml.replace(`<div id="app">`, `\n<div id="app">${html}`)
you have a mistake in client code - dynamically created component. It is just unique every render (that is the root cause)
+ const Simple = lazy(() => import("./pages/simple"));
....
- {renderPage(lazy(() => import("./pages/simple")))()}
+ {renderPage(Simple)()}
Please use dev
mode to debug the build - hydrate will complain about markup mismatch
"create-bundle:client": "cross-env NODE_ENV=development BABEL_ENV=client parcel build app/index.html -d dist/client --no-source-maps --no-minify --public-url /dist/client",
You can also check how its working using manual debugging
console.log('before', element.innerHTML);
ReactDOM.render(app, element);
console.log('after', element.innerHTML);
Thanks!!! So happy to find a fix.
One question though -- I made those changes (just pushed them) and I'm still seeing a warning:
Warning: Did not expect server HTML to contain a <h2> in <div>.
However, the before
and after
prints both show <h2>Simple simple simple</h2>
, so it seems like nothing changed. Shouldn't this warning be gone now?
(Note: your point number 4 used ReactDOM.render
but I assume you mean ReactDOM.hydrate
so I used that.)
Well, look like using Suspense
(LazyBoundary
) is the root cause for this.
lazy
to importer
- everything will work as beforeLazyBoundary
, which is no longer required - the problem will be solved.
👉 So the problem is bound to Suspense
boundary.I've downgraded your example back to 16.9.0
, and the problem was resolved once again. So here is the root cause - https://github.com/facebook/react/issues/16938 - which was expected to be resolved a while ago, but look like it strikes back.
According to this test - https://github.com/facebook/react/pull/16945/files#diff-ab371863932cd2e8f0ba14ff2eaab380R687 all you have to do (and it will fix the problem, I've tested) - remove fallback
prop from LazyBoundary
(typescript will complain).
Citing https://github.com/facebook/react/pull/16945
So technically a workaround that works is actually setting the fallback to undefined on the server and during hydration. Then flip it on only after hydration.
Not very happy to discover this problem a year after it occurred (still on 16.9 on projects using Lazy). Tested 16.10.1 - and it's completely broken (eating dom nodes, not just recreating them). 😭
Just checked:
fallback={undefined}
fallback
to something defined in useLayoutEffect
(for example)lazy
would throw (for example components which were not server side rendered) that would throw an error. So it all depends on how "full" page is rendered, and if some pieces were not rendered - why and how.Hmm, I tried switching to importedComponent
and got it to hydrate without any warnings. I just pushed those commits.
Is there any reason to prefer the lazy
API over the pre-lazy
API? Is pre-lazy
older or worse somehow? If importedComponent
works properly then I'd rather use that than try to work around issues with LazyBoundary
. But wondering what solution you'd recommend.
Lazy
gives you a Suspense
boundary, so you can "await" loading on a bit higher level, including using new useTransition
hooks to properly await for loading before actually changing the page.
It all depends on the use case, but if you have more that one deferred component on the page - you might prefer lazy
I'm getting a white flicker that happens when
ReactDOM.hydrate
is called. I've tried my best to follow the documentation but I can't figure out how to fix it.I trimmed down my project to a simple repro which you can find here: https://github.com/thomasjm/ric-hydrate-flicker
Any help would be much appreciated :)