Search code examples
reactjsthymeleafserver-side-rendering

Prevent React from re-render partial SSR content on hydrate


How can I prevent SSR content from re-rendered in the clients browser on React.hydrate(...)?

My Workflow

In my current project, I render a bunch of React components during my build process via ReactDomServer.renderToString(...). The result of this rendering will be used as a Thymeleaf fragment. The SSR DOM contains several th:text attributes for internalization:

Brief example

This is my component:

import React from "react";

class WdbThym extends React.Component {
    constructor(props) {
        super(props);
    }

    shouldComponentUpdate() {
        return false;
    };

    render() {
        return (
            <span {...{ 'th:text': `#{${this.props.i18n}}` }}>
                {this.props.i18n}
            </span>
        );
    }
}

export default WdbThym;

This is an example usage of WdbThym:

<WdbThym i18n="general.hello_world" />

This is what ReactDomServer.renderToString(...) creates:

<span th:text="#{general.hello_world}">general.hello_world</span>

This is what Thymeleaf renders and dispatches to the client:

<span>Hello World!</span>

This is what React.hydrate renders:

<span th:text="#{general.hello_world}">general.hello_world</span>

How can I prevent the initial rendering on React.hydrate(...) for the above mentioned Component?


Solution

  • As @estus reported, https://github.com/facebook/react/issues/8017 solved this problem.

    I've created a slightly more complex sample to reproduce the given advice:

    DOM

    <div id="root">
      <div class="App">
        <h1>THIS IS SSR CONTENT</h1>
        <p>Current Time: 2019-01-27T08:00:00.000Z</p>
        <p>Hello World from Thymeleaf (SSR)</p>
      </div>
    </div>
    

    React

    class CsrComponent extends React.Component {
      state = { currentTime: "" };
    
      componentDidMount = () => {
        setInterval(() => {
          this.setState({ currentTime: new Date().toISOString() });
        }, 1000);
      };
    
      render() {
        return <p>Current Time: {this.state.currentTime}</p>;
      }
    }
    
    class SsrComponent extends React.Component {
      render() {
        return (
          <p
            dangerouslySetInnerHTML={{ __html: "" }}
            suppressHydrationWarning
            {...{ "th:text": `#{${this.props.i18n}}` }}
          />
        );
      }
    }
    
    class App extends React.Component {
      render() {
        return (
          <div className="App">
            <h1>THIS IS SSR CONTENT</h1>
            <CsrComponent />
            <SsrComponent i18n="general.hello_world" />
          </div>
        );
      }
    }
    

    Hydrating

    const rootElement = document.getElementById("root");
    ReactDOM.hydrate(<App />, rootElement);
    

    This given example is also available at https://codesandbox.io/s/o5171l2v59