React: Hydration breaks the HTML (class)

Created on 23 Oct 2017  路  7Comments  路  Source: facebook/react

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

Most helpful comment

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 https://github.com/reactjs/reactjs.org/issues/25 and https://github.com/facebook/react/issues/10591 for more information about this.

All 7 comments

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 https://github.com/reactjs/reactjs.org/issues/25 and https://github.com/facebook/react/issues/10591 for more information about this.

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: https://github.com/facebook/react/issues/8017#issuecomment-256351955. Render what's on the server first and then setState in components that need to show something different. Hope this helps!

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 https://github.com/reactjs/reactjs.org/issues/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 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

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.

Was this page helpful?
0 / 5 - 0 ratings