Google Optimize JS API on SPA - javascript

I have a problem with the Google Optimize JS API on my SPA, the callback is only run on page reload but when navigating back and forward to the specific page, the callback isn't being run. Here's the scenario:
1) I reload the application and go to my page having an active A/B-test: mysite.com/testpage
2) The folowing code is being run:
gtag('event', 'optimize.callback', {
name: experimentId,
callback: useVariant,
});
and useVariant(variantId) is being called setting the correct variantId.
3) I click or navigate away from the page and then click/navigate back to mysite.com/testpage, this is a SPA mind you.
4) The gtag() code is being re-called, BUT the callback useVariant() isn't being run.
5) WTF.
6) If I reload the page, using CMD+R (yes, I'm using Mac), the code runs as in point 1) And everything is ok.
I have tried the following:
A. Resetting the datalayer using google_tag_manager['xxx'].datalayer.reset()
B. Re-pushing the event with undefined callback gtag('event', 'optimize.callback', undefined)
C. Resetting datalayer = [] manually
D. Removing the optimize.callback event from the datalayer
E. Any combination of above (which gives a bit interesting results sometime)
So the question is in point 5: WTF? Any suggestions/thoughts are greatly appreciated.
Thanks!

I finally solved it! Here's how to fix it, maybe this will help someone else.
The problem is the callback function useVariant. I had defined it as so useVariant(variantId) when apperantly (thanks Google documentation for being clear on this, NOT) it has to be useVariant(variantId, experimentId)
So by changing the callback function to take two arguments the code will work.

For React, you can implement Activation Events in your top-level component's componentDidMount and componentDidUpdate lifecycle methods to run the experiment codes immediately after the component mounts or updates on the page.
Your code may look like this:
import React from 'react';
export default class App extends React.Component {
componentDidMount() {
dataLayer.push({ 'event': 'optimize.activate' });
}
componentDidUpdate() {
dataLayer.push({ 'event': 'optimize.activate' });
}
render() {
// experiment code goes here...
}
}
SPA tests on Google optimize

Related

How to get document element when I use external script in react?

I'm using React to build my project. I made a chat button by using external script. And I'd like to disappear that button in some specific pages. I can't use a ref to access that button's element So I used document.getElementById.
But my problem is my code sometimes returns error. (I think when my code runs, chat button didn't run by external script.) How can I solve this problem?
useEffect(() => {
//access chat button element by using document.getElementById
const chatBtn = document.getElementById('ch-plugin-launcher');
//if it doesn't exist in current page, it returns.
if (!chatBtn) {
return;
}
//if a button exists, it will be hide.
chatBtn.classList.add('hide');
return () => {
chatBtn.classList.remove('hide');
};
}, []);
I think the error in return of useEffect. You return a function, which can be called at any time whenever the chat button does not exist. Add check for existing on the chat button in the useEffect return function. Other code looks well.
useEffect(() => {
// Your code
return () => {
document.getElementById('ch-plugin-launcher')?.classList?.remove('hide');
};
});
I think #0x6368656174 answwer is correct.
Just for more clarification why:
When exactly does React clean up an effect? React performs the cleanup
when the component unmounts. However, as we learned earlier, effects
run for every render and not just once. This is why React also cleans
up effects from the previous render before running the effects next
time. We’ll discuss why this helps avoid bugs and how to opt out of
this behavior in case it creates performance issues later below.
Source: https://reactjs.org/docs/hooks-effect.html#example-using-hooks-1

What happens to the code that is written after execution of history push in React?

I generally use the Redirect component for routing. As we need to return the Redirect, it is pretty understandable. However, I am confused with using history objects. Actually in my code base after pushing some route into the history stack. There are so many actions and functions and dispatchers are being called.
When would the new routes component be mounted? If someone could not understand please see the below sample code.
const some_handler_func = () => {
history.push('/component 2');
setState('dummy');
console.debug('checking')
//what happens to the above two lines.
}
The console.debug will run as intended, but state change to your component might not work, because the component you try to change state on might be already unmounted.

React SSR: event listeners and render are not added in server-rendered code?

I've done some work with SSR but mainly using NextJS -- I understand why the code is being rendered (first, at least) on the server (to show the user a screen quickly, to allow search engines to parse SPAs), and to some extent how it's being done (some engine on the server side will take the SPA and convert it to HTML and then sent it 'down' to the browser).
What I don't understand is what the step-by-step process of SSR entails. I've been working through a Front End Master course on intermediate React that briefly touches on Reactdomserver -- I understand the concept: convert the SPA elements to strings, and send them on to the browser. The course says that 'event listeners are added later' (I suppose once React takes over), and that render isn't available on the server. I believe I understand the second part -- I suppose the React javascript just isn't available until React is up and running -- but don't understand what the first statement regarding event listeners actually means. When I wrote old-school HTML code, loaded it to a server, and it was downloaded to a browser, complete with whatever event listeners I had, and it just worked. What is SSR doing differently than we did in the old days writing non-SPA sites? I suppose it's that the DOM isn't available on the server, so you can't add event listeners until the HTML is rendered in the browser and the DOM is built -- but as nothing is displayed until the DOM is built, why even talk about 'adding event listeners later'?
Thanks for any help you can provide!
Let's take a very contrived example React Component:
function App() {
const [content, setContent] = useState("click me!");
return <div onClick={() => setContent("you did it")}>{content}</div>
}
Now, on the server, we want to generate HTML from it. For that, we mimic useState to return the initial state, call App(), and arrive at a JSX tree that looks like:
{ type: "div", props: { onClick: () => ..., }, children: ["click me!"] }
Now we can turn that into a string of HTML easily ...
<div>click me!</div>
and send that to the client. But wait, were did the onClick go? Well, we could not add it. For sure we could (1) add an inline onclick handler, or (2) register some event listener later, but the function was instantiated on the server, we can't just serialize it and send it to the client.
<script>
getElement("App").onClick(() => {
setContent("you did it!"); // Um ... Were does it refer to?
});
</script>
Now what to do? Well, we could take the React Component again, and turn it into a piece of JS, that is intended to run on the frontend as a whole. Then we can attach that piece of JS to the HTML we sent to the client. Now we run the same code on the frontend, but this time we do have access to the DOM, we can register handlers, and we can rerender if setState gets called. Therefore App() gets called again, but from the JSX returned ...
{ type: "div", props: { onClick: () => ..., }, children: ["click me!"] }
... we can directly generate Elements now, and attach the handler:
const el = document.createElement(jsx.type);
el.addEventListener("click", jsx.onClick);
el.appendChild("click me!");
And all that is exactly what NextJS does for you.

if I add anything inside componentWillUnmount nothing happening

I am trying to learn react myself.
I am trying to set session inside my componentWillUnmount.
but if I add anything inside componentWillUnmount nothing happening.
i debugged by adding console and debugger still nothing happening.
can you guys tell how to make my componentWillUnmount to work
so that in future I will fix it myself.
providing my relevant code snippet and sandbox below.
all my code is in RecipeReviewCard.js
https://codesandbox.io/s/1vqkz1own7
componentDidMount() {
console.log("componentDidMount---->");
}
componentWillUnmount() {
console.log("componentwillUnmount---->");
debugger;
window.sessionStorage.setItem(
"favoriteValues",
JSON.stringify(this.props.benchMarks)
);
}
As the name suggests, 'componentWillUnmount' will fire when the component is about to be taken out of the DOM (eg. when you switch tabs for example) and in your example the console.log indeed does fire. Use 'componentWillMount' to run the function when the component is loaded into the DOM

setOnNavigatorEvent callback not fired when switching tabs

I am working on an app powered by react-native#0.55.4 and react-native-navigation#1.1.474.
Initially, there is only a login screen (using Navigation.startSingleScreenApp). When the user logs in I call Navigation.startTabBasedApp (one of the tab components is my NavScreen). Whenever the user changes to another tab the root of the tab's stack is supposed to be displayed so I tried something like this:
class NavScreen extends React.PureComponent {
constructor(props) {
super(props)
this.props.navigator.setOnNavigatorEvent(this.toRootOnTabSelect.bind(this))
}
toRootOnTabSelect(event) {
const {id} = event
if (["bottomTabSelected", "bottomTabReselected"].includes(id)) {
this.props.navigator.popToRoot({
animated: true,
animationType: "fade",
})
}
}
render() {
return <Text>Whatever...</Text>
}
}
But for some reason my toRootOnTabSelect event handler method is not being called when I change tabs (by clicking on them - not by calling the switchToTab API method).
There are multiple posts I found online (i.e. https://stackoverflow.com/a/51159091/6928824, https://github.com/wix/react-native-navigation/issues/648) that indicate that it should work so I don't know what I'm missing. :/
Any help is greatly appreciated! :)
One of the reasons that can cause that is using setOnNavigatorEvent in conjuction with addOnNavigatorEvent, if you have a screen wrapper component that implement addOnNavigatorEvent your current listener will not work.
As mentioned in documentation
setOnNavigatorEvent Can not be used in conjuction with addOnNavigatorEvent
Also
Bear in mind that you can't use both addOnNavigatorEvent and setOnNavigatorEvent. addOnNavigatorEvent returns a function, that once called will remove the registered handler.
I would suggest trying addOnNavigatorEvent instead of setOnNavigatorEvent
This seems to be a bug in react-native-navigation#^1.1.474 (note the caret): see my issue on GitHub.
An according pull request has been opened but not yet merged. :(

Categories

Resources