Fix Google AdSense loading issues with React

Photo by Webaroo.com.au on Unsplash

On one of my recent client projects, I encountered a problem with loading Google AdSense ads.

As you will understand, I am using React to develop this site. One of the peculiarities of React is that it is able to optimize its modifications to the DOM. It updates only what is necessary. It’s top.

Except in the case of Google AdSense. It’s less top.

How does AdSense Ad Loading work?

Let’s take a look at the code Google is asking us to add to our page to load an advertisement.

<ins class="adsbygoogle"
style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-1234567890123456"
data-ad-slot="1234567890"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

The ad will appear where we put the ‘<ins> </ins>’ tag.

Then, you must notify the Google AdSense script that there is an advertisement to display. That’s the job of the JavaScript mini line in the example above. Basically, this line adds an empty object to an array called ‘adsbygoogle’.

Why? As the loading of the Google JS script is asynchronous, to know that there are X advertisements to display, it uses this table. Reading the table when the scripts are loaded lets you always know how many ads are needed.

Once the Google script is loaded, in addition to reading the table, it will replace the ‘push’ method of this table to directly load the advertisement when you do ‘adsbygoogle.push ({})’.

The script will not check the array at regular intervals. It is called when (and only when) we call ‘.push’.

Display An Ad With React

The React version of this code doesn’t change much. Look :

|const Ad = props => {
useEffect(() => {
window.adsbygoogle = window.adsbygoogle || []
window.adsbygoogle.push({})
})

return (
<div>
<ins
className="adsbygoogle"
style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-1234567890123456"
data-ad-slot="1234567890"
/>
</div>
)
}

We find the tag ‘<ins />’ and the ‘.push ({})’. The push is done in a ‘useEffect’ so that it is executed as soon as the HTML code is rendered.

The ‘useEffect’ also allows you to re-execute the ‘push’ if the HTML code is rewritten by React for one reason or another. Which is interesting because we don’t load ads directly into this component. We just create ‘<ins />’ elements in the DOM and request (via ‘push ({})’) the Google script to display an advertisement there.

So if for some reason React deletes the ‘<ins />’ and re-creates it, we ask Google’s script to load a new advertisement into it using ‘useEffect’.

The error to shoot down

Unfortunately, this optimized behavior of React can cause us a new error:

|uncaught exception: TagError: adsbygoogle.push() error: All ins elements in the DOM with class=adsbygoogle already have ads in them.

Google checks that we are not asking for too many advertisements in relation to the places available on the page.

I have long sought a solution to this problem because I did not understand the error: I have exactly a ‘.push ({})’ for an ‘<ins />’ in my code. I don’t see how I could call the ‘.push ({})’ method too much.

Well, it was React who did anything. Or almost.

React Is Optimized And It’s (Almost) Always Great

Let’s go back to React’s optimized way of modifying the DOM.

Several events external to a component can indicate to React that it is necessary to update this component. For example a change in the props of the parent component, etc.

When React wants to update a component it executes the life-cycle methods of the component including its render method. It thus retrieves the tree of components that descend from this component. For the “component functions”, it is a question of executing the hooks and of recovering the return of the component function.

It does the same for each level of the component hierarchy. React ends up with the complete tree that represents our page. We also call it the virtual DOM. React will now compare the virtual DOM to the DOM and detect the differences. Thus it can make only the necessary modifications and not a complete update of the DOM to gain speed.

And that describes our mistake. The hooks/methods of the life-cycle of a component are always executed but the DOM does not necessarily change. The code for our ‘<Ad />’ component is very simple, it rarely needs to be updated. On the other hand, external events can cause React to execute the hooks/methods of the life-cycle for an update.

The ‘.push ({})’ is therefore sometimes executed in cases where the part of the DOM concerning our advertising will not be updated. As the DOM is not updated, an advertisement is already present in the component’s ‘<ins />’tag. The Google Analytics code, therefore, raises an error.

Update Component Only When Necessary

To fix this, we should no longer let React decide (guess) for itself when to update our advertising component.

Personally, I want the ads to be updated when the user changes pages. For any other change on the page, the component tree should not be updated.

|const Ad = props => {
const { currentPath } = props

useEffect(() => {
window.adsbygoogle = window.adsbygoogle || []
window.adsbygoogle.push({})
})

return (
<div key={currentPath}>
<ins
className="adsbygoogle"
style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-1234567890123456"
data-ad-slot="1234567890"
/>
</div>
)
}

Using the `key` property of components

The first modification to make is to use the ‘key’ property of the JSX ‘<div />’ element. The purpose of this property is to tell React which elements have changed, been added, or removed from the DOM. It thus gains in speed in its treatment of the virtual DOM because it does not have to determine itself the changes carried out at this place.

In other words, in our case, as long as the key does not change React will not try to update the advertisement. On the other hand, the change of key will force React to modify the DOM and therefore re-generate the advertisement.

I use the ‘path’ of the current page as a key, which forces React to recreate the ad tags when the user changes pages but prevents him from making changes as long as the user remains on the same page.

We now know for sure when the DOM will be updated. But the error still exists since hooks are always executed too often.

|const Ad = props => {
const { currentPath } = props

useEffect(() => {
window.adsbygoogle = window.adsbygoogle || []
window.adsbygoogle.push({})
}, [currentPath])

return (
<div key={currentPath}>
<ins
className="adsbygoogle"
style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-1234567890123456"
data-ad-slot="1234567890"
/>
</div>
)
}

Reduced frequency of hook execution

The last modification to our component resides in the second argument of ‘useEffect’. We pass it a table of values to check before executing. Until the values in this array have changed, the hook will not be executed.

Using the same ‘currentPath’ property, I synchronize the execution of the hook with the update of the DOM. The error is resolved.

In the case of “component classes”, the equivalent is achieved by overriding the ‘shouldComponentUpdate’ method.

|class Ad {  
shouldComponentUpdate(nextProps) {
return this.props.currentPath !== nextProps.currentPath
}

componentDidUpdate() {
// .push({})
}

render() {
// <ins />
}
}

Reduced frequency of execution of life cycle methods

In the latter case, the ‘key’ property of the ‘<div />’ element may not be used because the render method is not called at all when ‘shouldComponentUpdate’ returns ‘false’.

Read,write, repeat...