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).
This is the HTML being served by the server (before hydration):
This is the HTML being rendered by React, when we disable SSR:
This is the invalid HTML being rendered by React when it tries to hydrate 1) with 2). The expected HTML is 2).
Chrome 61.0.3163.100
React v16.0.0
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 馃拃 ).
We'll track better docs in https://github.com/reactjs/reactjs.org/issues/25 and better warnings in https://github.com/facebook/react/issues/10085.
@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 incomponentDidMount()
. 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.
Most helpful comment
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.