styled-jsx icon indicating copy to clipboard operation
styled-jsx copied to clipboard

SSR vs Browser results in different classnames and ReactDOM.hydrate mismatch :(

Open cphoover opened this issue 4 years ago • 3 comments

Bug

What is the current behavior?

Summary: I'm converting my site into a static generated site. I'm using ReactDOMServer.renderToString and flush from 'styled-jsx/server' in a node.js pipeline to render the static assets. Then when the client runtime loads I am using ReactDOM.hydrate to attempt to hydrate the application without a full render. Unfortunately it is re-rendering and a big white flash happens....

I investigated by doing a diff on the HTML of my application without JavaScript enabled against the HTML after the ReactDOM.hydrate step occurs and the application is re-rendered. The only differences I see are the styled-jsx classnames being different... How are hashes being generated? Are they deterministic? Do they take into account logical components as well as dom-elements in their generation?

Is it possible to have the following output where everything is identical except for the styled-jsx classnames:

<a href="#main-content" tabindex="0" class="jsx-998058316 ada-skip-link">Skip to content</a>
<div data-metrics-location="HDR" class="jsx-3222006434 global-utility-navigation-wrapper">
<a href="undefined#main-content" tabindex="0" class="jsx-998058316 ada-skip-link">Skip to content</a>
<div data-metrics-location="HDR" class="jsx-2836309959 global-utility-navigation-wrapper">

notice the class on the .global-utility-navigation-wrapper has the class jsx-3222006434 in the first example but jsx-2836309959 in the second example.

This causes a mismatch and rerender when trying to rehydrate the page.

image

cphoover avatar Apr 01 '21 11:04 cphoover

The hash should be deterministic.

See https://github.com/vercel/styled-jsx/blob/master/src/_utils.js#L192-L228

and https://github.com/vercel/styled-jsx/blob/master/src/stylesheet-registry.js#L134-L150

Do you have dynamic styles? The issue could also be due to the fact that you are hydrating multiple roots. Currently the styles registry is a singleton but there is a PR to enable multiple registries https://github.com/vercel/styled-jsx/pull/703

giuseppeg avatar Apr 01 '21 12:04 giuseppeg

@giuseppeg hmmm after digging deeper I discovered more...

(I compared the strings being passed into the hashString function and the resultant hash)

I realize that when I call flush it is actually passing a different string to the hashString function. When I inspect the strings using diff I realize that the css passed to hashString in the the build + babel transpilation process do not include the webkit prefix however the SSR flush actually includes a -webkit- vendor prefix on certain css rules when passed to hashString

image

Above you can see a csv surrounded with quotations with columns Filename of styles, Resultant Hash, Input text You can see how their is a difference in the hash and the vendor prefix

image ^^ In many other parts of places the same thing is happening, and it seems to be the only difference between the two input strings (the -webkit- prefix)!

cphoover avatar Apr 12 '21 06:04 cphoover

> Do you have dynamic styles?

No I searched through the project and there are no dynamic styles.

The issue could also be due to the fact that you are hydrating multiple roots

@giuseppeg I changed it so they are both hydrating the same root, and it still happens. I will try and recreate in a simplified example and post

cphoover avatar Apr 14 '21 22:04 cphoover