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

Flicker on hydrate when using Parcel SSR #201

Open
thomasjm opened this issue Sep 9, 2020 · 8 comments
Open

Flicker on hydrate when using Parcel SSR #201

thomasjm opened this issue Sep 9, 2020 · 8 comments
Assignees
Labels

Comments

@thomasjm
Copy link

thomasjm commented Sep 9, 2020

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 :)

@theKashey theKashey self-assigned this Sep 9, 2020
@theKashey
Copy link
Owner

👍 will take a look

@theKashey
Copy link
Owner

theKashey commented Sep 10, 2020

  1. 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}`)
  1. 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)()}

3. 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",


4. You can also check how its working using manual debugging
```js
console.log('before', element.innerHTML);
ReactDOM.render(app, element);
console.log('after', element.innerHTML);

@thomasjm
Copy link
Author

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.)

@theKashey
Copy link
Owner

Well, look like using Suspense (LazyBoundary) is the root cause for this.

  • if you change lazy to importer - everything will work as before
  • if you will after that remove LazyBoundary, 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 - facebook/react#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).

@theKashey
Copy link
Owner

Citing facebook/react#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). 😭

@theKashey
Copy link
Owner

Just checked:

  • you can render all boundaries with fallback={undefined}
  • and update fallback to something defined in useLayoutEffect (for example)
  • however is any 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.

@thomasjm
Copy link
Author

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.

@theKashey
Copy link
Owner

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants