I'm in a muddle about React, useEffect, and adding callback functions to event listeners. These listeners being registered to Google Publisher Tag events, and I'm trying to get to the outcome where I can use parameters passed (display
in my example code) and the event object in my callback function.
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<script src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"></script>````
const { useEffect } = React;
const slotOnloadCallback = (display, event) => {
const { slot } = event;
console.log(`callback called for slot ${slot.getSlotElementId()} - display: ${display}`);
};
const create = (id, display) => {
window.googletag = window.googletag || {cmd: []};
window.googletag.cmd.push(function() {
const slot = window.googletag.defineSlot('/1234567/example', [728, 90], id);
slot.addService(googletag.pubads());
window.googletag.pubads().enableSingleRequest();
window.googletag.enableServices();
});
window.googletag.pubads().addEventListener("slotOnload", slotOnloadCallback.bind(null, display));
window.googletag.cmd.push(function() {
window.googletag.display(id);
});
}
const Ad = (props) => {
const { id, display } = props;
console.log(`Ad ${id}: Rendering`);
useEffect(() => {
console.log(`Ad ${id}: Mounted`);
create(id, display)
}, []);
return (
<div id={id}></div>
);
};
function App() {
return (
<div>
<Ad
id="slot-1"
display="full"
/>
<p>...</p>
<Ad
id="slot-2"
display="partial"
/>
</div>
)
}
ReactDOM.render(<App />, document.querySelector("#app"));
The event listeners are working, but when I bind the variable I require to the callback, the callback ends up triggered multiple times (* the number of Ad components are rendered). I think I understand that binding variables to a function makes it unique, and therefore can be registered multiple times to one listener, but I thought that using useEffect got around this... :(
I am getting the output:
"Ad slot-1: Rendering"
"Ad slot-2: Rendering"
"Ad slot-1: Mounted"
"Ad slot-2: Mounted"
"callback called for slot slot-1 - display: full"
"callback called for slot slot-1 - display: partial"
"callback called for slot slot-2 - display: full"
"callback called for slot slot-2 - display: partial"
... Of which I'm confused about how slot-1
is even receiving partial
.
Is there a way I can ensure the event listener only fires once per component? I have tried moving the callback definition into the component's useEffect
block, and also defining it in App
and passing it to the component as a prop, but these have all had the same outcome.
Any help would be massively appreciated!
useEffect
runs twice in dev mode. You have to take care of removing the listeners