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

Need a way to prevent component render on client side initial load #8017

Closed
scsherwood opened this issue Oct 19, 2016 · 20 comments
Closed

Need a way to prevent component render on client side initial load #8017

scsherwood opened this issue Oct 19, 2016 · 20 comments

Comments

@scsherwood
Copy link

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.

@jblok
Copy link

jblok commented Oct 25, 2016

@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.

@roastlechon
Copy link

@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?)

@jblok
Copy link

jblok commented Oct 25, 2016

  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.

@roastlechon
Copy link

roastlechon commented Oct 25, 2016

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.

@scsherwood
Copy link
Author

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: [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
#8017 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AEasemDdfdnGKbHGiTdVUhF7jnoO8vWyks5q3h3CgaJpZM4Ka_m4
.

@pjuke
Copy link

pjuke commented Oct 26, 2016

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.

@gaearon
Copy link
Collaborator

gaearon commented Oct 26, 2016

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.

@gaearon gaearon closed this as completed Oct 26, 2016
@roastlechon
Copy link

Ill prepare a sample repo to illustrate situation

@roastlechon
Copy link

roastlechon commented Oct 27, 2016

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

@vsnig
Copy link

vsnig commented Apr 23, 2017

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

@luisherranz
Copy link

@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 :)

@roastlechon
Copy link

@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).

@luisherranz
Copy link

@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.

@silouanwright
Copy link

silouanwright commented Mar 15, 2018

@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.

@eliseumds
Copy link

eliseumds commented Mar 16, 2018

@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.

@itssumitrai
Copy link

@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.

@luisherranz
Copy link

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

@gaearon
Copy link
Collaborator

gaearon commented Jun 16, 2018

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.

@luisherranz
Copy link

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

@ahaurw01
Copy link

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.

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