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

Hydration breaks the HTML (class) #11336

Closed
SamyPesse opened this issue Oct 23, 2017 · 7 comments
Closed

Hydration breaks the HTML (class) #11336

SamyPesse opened this issue Oct 23, 2017 · 7 comments

Comments

@SamyPesse
Copy link

We've updated an application to React 16, this app is using react-responsive.

Since we've updated to React 16, we have bugs related to invalid HTML being outputted by React hydration.

Our server generates an HTML for the desktop, but if the client has a mobile, the rendering mismatch. Previously it was causing a full re-render by ReactDOM.render, it was not perfect, but it worked.

With React 16, ReactDOM.hydrate is supposed to not-rerender everything but only the mismatching parts. But after we run ReactDOM.hydrate, the HTML is an invalid mix of both (class are sliced).

1) HTML generated by the server (desktop mode)

This is the HTML being served by the server (before hydration):

screen shot 2017-10-23 at 11 35 11

2) HTML generated by the client without SSR (normal mobile mode)

This is the HTML being rendered by React, when we disable SSR:

screen shot 2017-10-23 at 11 39 07

3) Wrong HTML generated by hydration of 1) in client (mobile mode)

This is the invalid HTML being rendered by React when it tries to hydrate 1) with 2). The expected HTML is 2).

screen shot 2017-10-23 at 11 36 46

Versions

Chrome 61.0.3163.100
React v16.0.0

@gaearon
Copy link
Collaborator

gaearon commented Oct 23, 2017

With React 16, ReactDOM.hydrate is supposed to not-rerender everything but only the mismatching parts

This is not a correct understanding. With React 16 it is not supported to render different things on secret and the client for performance reasons. You need to fix the cases where markup differs.

React 16 does patch up in some cases (such as text content) but it will not attempt to patch up attributes. Text content is an exception here because it is often different from the server due to, for example, timestamps.

See reactjs/react.dev#25 and #10591 for more information about this.

@gaearon gaearon closed this as completed Oct 23, 2017
@gaearon
Copy link
Collaborator

gaearon commented Oct 23, 2017

Note that if you're okay with an extra re-render and really need to render something different on the client, you can use this approach: #8017 (comment). Render what's on the server first and then setState in components that need to show something different. Hope this helps!

@SamyPesse
Copy link
Author

Thanks 🍻 the new behaviour of React 16 makes sense!

I was confused by the documentation:

React will attach event listeners while preserving as much of the existing DOM as possible. For best results, you should try to render the same content on the server as on the client, with as few differences as possible.

I guess it'll be fixed by reactjs/react.dev#25 :)

Maybe the warning in the console can be improved, currently it logs something like Warning: Expected server HTML to contain a matching <svg> in <div>., it could be an error log with precisions that we should fix the cases where markup differs.


I'll find a solution to stop using react-responsive and use CSS media queries instead (JS media queries between server and client are always hell 💀 ).

@gaearon
Copy link
Collaborator

gaearon commented Oct 23, 2017

We'll track better docs in reactjs/react.dev#25 and better warnings in #10085.

@gaearon
Copy link
Collaborator

gaearon commented Oct 23, 2017

reactjs/react.dev#200

@Tom910
Copy link
Contributor

Tom910 commented Nov 10, 2017

@gaearon same problem. Maybe there is an opportunity to say to the react that it rerender this block?

Otherwise we will have to roll back to 15 react

@gaearon
Copy link
Collaborator

gaearon commented Nov 10, 2017

As it says in the documentation:

React expects that the rendered content is identical between the server and the client. It can patch up differences in text content (such as timestamps), but you should treat mismatches as bugs and fix them. In development mode, React warns about mismatches during hydration. There are no guarantees that attribute differences will be patched up in case of mismatches. This is important for performance reasons because in most apps, mismatches are rare, and so validating all markup would be prohibitively expensive.

If you intentionally need to render something different on the server and the client, you can do a two-pass rendering. Components that render something different on the client can read a state variable like this.state.isClient, which you can set to true in componentDidMount(). This way the initial render pass will render the same content as the server, avoiding mismatches, but an additional pass will happen synchronously right after hydration. Note that this approach will make your components slower because they have to render twice, so use it with caution.

Remember to be mindful of user experience on slow connections. The JavaScript code may load significantly later than the initial HTML render, so if you render something different in the client-only pass, the transition can be jarring. However, if executed well, it may be beneficial to render a "shell" of the application on the server, and only show some of the extra widgets on the client. To learn how to do this without getting the markup mismatch issues, refer to the explanation in the previous paragraph.

Hope this helps.

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

No branches or pull requests

3 participants