React: Need a way to prevent component render on client side initial load

Created on 19 Oct 2016  ·  20Comments  ·  Source: facebook/react

For Ad performance in an isomorphic application we need React to avoid rendering an Ad Component containing an ad position div on the client initial load. Here is the following scenario:

  1. React renders the ad position div on the server and sends html to client. This div is an empty div with an id attribute identifying the location to place an ad.
  2. Google gpt makes a request for ad before React lifecycle is finished on client, and the ad position div gets filled with an ad.
  3. React finds a mismatch with server and client ad position div, so it re-renders the empty ad position div on client wiping out the ad.

If there is a way to conditionally stop React from rendering the ad position div on the client, we have not found it.

Seems like there needs to be a flag or method in the react lifecycle that can be used to prevent a component render on certain conditions.

Most helpful comment

Use state for this:

class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.state = { hasMounted: false };
  }

  componentDidMount() {
    this.setState({ hasMounted: true });
  }

  render() {
    return (
      <div>
        <p>This text is rendered on the server.</p>
        {this.state.hasMounted && <p>This text only appears after mounting.</p>}
      </div>
    );
  }
}

Generally we don't recommend setState() in componentDidMount() because it causes an extra re-rendering, but in your case this sounds exactly like what you need.

All 20 comments

@scsherwood Can't you just make the Google gpt request in componentDidMount, and then rerender with the results of this call. So react will finish 'rendering' the same markup as was sent from the server, and the ad request can happen separately after initial rendering is complete.

@jblok the reason why we cant make it in componentDidMount is because:

  1. its tied up in React lifecycle
  2. the script for the component is at the bottom of the page (after server side rendered markup)
  3. we need to have ads appear immediately on page load (we already have server side rendered markup, so why delay ads showing up to the client side scripts?)
  1. Yes, but so is your entire app so this shouldn't be a downside really.
  2. I'm unclear - do you mean the script is a third party ad script which just runs on page load regardless?
  3. Loading the ads on componentDidMount won't slow you down any further. As you stated, the server is sending an empty div anyway.

Yes, but so is your entire app so this shouldn't be a downside really.

Visually speaking, the app is present on first byte. The lifecycle on the client side ties up firing the gpt request.

I'm unclear - do you mean the script is a third party ad script which just runs on page load regardless?

The script is the bundled javascript (react, miscellaneous vendor scripts, app scripts, etc). GPT is loaded in the head of the document.

Let me know if you have any more questions to clarify.

Our goal is to visually render a full page with ads under ~2-3 seconds.

We are trying to get out in front of the React render to get faster
performance out of the ads. Waiting on react costs us around 500 to 1000
ms before the ad call is made.

_Steven *_Sherwood *
_w:_ 770-226-2527 _e:_ steven.[email protected]
http://weather.com/apps http://weather.com/apps
http://weather.com/apps http://weather.com/apps
http://weather.com/apps http://weather.com/apps

On Tue, Oct 25, 2016 at 11:18 AM, Jonathon Blok [email protected]
wrote:

@scsherwood https://github.com/scsherwood Can't you just make the
Google gpt request in componentDidMount, and then rerender with the results
of this call. So react will finish 'rendering' the same markup as was sent
from the server, and the ad request can happen separately after initial
rendering is complete.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/facebook/react/issues/8017#issuecomment-256065424,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AEasemDdfdnGKbHGiTdVUhF7jnoO8vWyks5q3h3CgaJpZM4Ka_m4
.

Another option may be to have an off-screen <div> rendering the ad from the server-state at load time and push the rendered html into your store.

Then at the components componentDidMount you simply grab the html from store and set it inside a <span> in your target component using https://facebook.github.io/react/docs/dom-elements.html#dangerouslysetinnerhtml

This assumes you can determine when your ad service has finished rendering.

Use state for this:

class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.state = { hasMounted: false };
  }

  componentDidMount() {
    this.setState({ hasMounted: true });
  }

  render() {
    return (
      <div>
        <p>This text is rendered on the server.</p>
        {this.state.hasMounted && <p>This text only appears after mounting.</p>}
      </div>
    );
  }
}

Generally we don't recommend setState() in componentDidMount() because it causes an extra re-rendering, but in your case this sounds exactly like what you need.

Ill prepare a sample repo to illustrate situation

Recently opened up a discussion with Kevin Lacker, and tried to reproduce our application on a smaller example repo, and was not able to reproduce the ads wipeout issue.

https://github.com/roastlechon/react-component-wipeout-example

My theory is that one of our libraries that we are using is causing a rerender, which causes the warning message for

React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server

I will continue to do some digging to determine what exactly is the library causing issues

@scsherwood have you found the solution? I have exactly same issue - DFP ad disappears when React client app has been loaded

@snegostup @roastlechon we have the same problem with GPT/AdSense ads.

If we wait to show the first add until React is loaded (around 2 seconds) it destroys the viewability of that first ad.

We need it to load the ad before React is loaded. That's really simple using SSR, but React wipes it out in the hydration phase.

We are surprised there isn't any solution or workaround for this yet, so any insight is really welcomed :)

@luisherranz we were able to diagnose and figure out that it was a hydration mismatch that occurred which wiped out the dom in hydration.

GPT is able to load beforehand with the server side rendered markup and on hydration nothing is wiped out (after we fixed the issue with mismatch).

@roastlechon thanks for your answer.

Our error is:

Warning: Did not expect server HTML to contain a <ins> in <ins>.

We only render one <ins> in React. The other <ins> tag inside it is rendered by the adsbygoogle.js library before React is loaded.

So our mismatch is not really happening between our server and client code, but from our sever code, with the additional tags created by adsbygoogle, and the client code after React finally loads.

@roastlechon wait can you elaborate? Hydration should blow everything away, no? If your ad renders after server side render but before client render, why would the client hydration honor the ad and keep it there?

@gaearon That makes sense but in this case, we want to suppress React client hydration totally as in our example, the html has updated between server & client render.

@reywright it will keep it there if React renders exactly the same thing on both sides. For example:

const { id } = this.props;

return (
  <div
    suppressHydrationWarning
    dangerouslySetInnerHTML={{__html: `<div id="${id}"></div>`}}
  />
);

Browser renders the ad even before this code loads, client-render-phase happens but doesn't touch the previously-created DOM nodes since that React DIV has the same properties.

@eliseumds Thats fine, but the third party ads library might be adding multiple dom nodes, adding some dom attributes, etc. The Application has no way of knowing that beforehand, so its not possible to have the same markup rendered on the server side.

@scsherwood any luck ?

We facing the exact same issue, our ads library modifies the Dom just before React client side hydration. React 15 was fine with it, but React 16 goes and removes the Dom Nodes altogether. We don't want to wait till componentDidMount to go and actually render the ads dom nodes, because its freaking slow. I think there should be some way to tell React: don't update this dom, I'll manage it myself. There are lots of use cases in real world applications for this requirement.

Another vote for the don't update this dom, I'll manage it myself option 👍

Note we generally don’t read discussions in closed issues. If this is still relevant please file a new issue with a more focused description of what you’re struggling with, and some demos.

@gaearon wouldn't it be better to reopen this one?

For anybody finding this thread and still having this problem, as of React 16.1.1 this very helpful comment on a related RFC seems to be exactly what you're looking for, similar to what @eliseumds was proposing.

<div
    dangerouslySetInnerHTML={{ __html: '' }}
    suppressHydrationWarning
/>

Note that the current behavior of dangerouslySetInnerHTML is to keep the current content when the content being set is different. In combination with suppressHydrationWarning it produces the desired behavior already.

Was this page helpful?
0 / 5 - 0 ratings