Google Optimize custom activation event tracking across multiple pages - google-analytics

I am implementing an A/B test with Google Optimize, using a custom activation event. I render different versions of the same React component based on which ID variant I receive from Optimize.
const getVariant = async () => {
if (window.dataLayer) {
await window.dataLayer.push({ event: 'optimize.activate' });
}
const intervalId = setInterval(() => {
if (window.google_optimize !== undefined) {
const variant = window.google_optimize.get(myExperimentId);
setUseTestVariant(Number(variant));
clearInterval(intervalId);
console.log(variant)
}
}, 100);
};
So far this works for randomly generating a different version of the same component corresponding to a variant in my Optimize experiment. But the main objective of the test is to track conversation rate through purchases on the checkout page, where the component which fires the event is not located. So my question is: if the custom activation event is triggered on one page and a session is started, does Optimize know when the user clicks through to the checkout page, or would that page also require an activation event to be pushed to the datalayer? Is it only possible to track interactions on the page that the event was fired from?

Optimize set the _gaexp cookie in your browser, in this cookie stores the experiment variant. This value is stored in google analytics as session scope, therfore, all custom events and transactions in this session will be attached with this variant. You don't need to activate the experiment in another page to save the transaction with the variant.
On the other hand, I don´t know your experiment, but it is usually not a good idea to set Transactions as primary objective. Transactions, in general, are secundary objetives. As primary objetive it is better to use the click in the component that you has changed, for example... add to cart, but depends of the experiment of course

Related

Sending requests without losing data & keep chronological order

I have a reactJS webapp that uses redux saga & axios to send
web requests for selecting products.
It uses takeEvery for the saga at the moment.
The problems:
Selecting products: If users select products very fast (by clicking checkboxes) there is request data missing. For instance: user clicks products: a,b,c,d,e,f but b & c are for instance not selected.
Toggling: I have tried to use the takeLatest but in case users select & deselect products very fast, there is a race condition / the order is not preserved.
As an example: user really quickly selects: product a, cancels it, selects it, cancels it, selects it
and the final status is 'cancel' although the last action was selecting it.
How it should work:
Selecting products: If a user selects products very fast, all of them should correctly be added.
Example: user adds a,b,c,d,e,f,g,h,i so all of these should be added.
Toggling: If a user toggles something repetitive & quickly, the final status has to match the latest action
As an example: User selects, cancels, selects, cancels => final state is cancel
Moreover: User selects => cancels => selects => final state id select
What is the appropriate technical way to implement this ?
You might be unwittingly opening a can of worms here, depending on how much of a perfectionist you are and how reliable you need your app to be. And if you want optimistic updates, oh boy.
Things can get way more difficult, because HTTP requests are not necessarily guaranteed to arrive in order nor are the responses from the server.
Same goes for your database, which can run into concurrency issues and so you might want to start versioning your data.
There are some strategies you can use to reduce such problems.
Write your endpoints in non stateful way or minimize the amount of sent state. For example, instead of sending list of all selected items, just send the single item that should be toggled.
Take advantage of the throttle & debounce saga effects, they will help you with request delays, skips and distribution to minimize the chances of stuff going out of order.
Consider blocking user action until you get a response back where it makes sense
Be aware that canceling a saga (e.g. through the takeLatest effect) doesn't cancel the ajax request itself (it will still arrive on your BE no matter what). It will only cancel the saga itself (in other words it will prevent your app from running the code after you would receive the data from BE)
Also be aware that using takeLatest to cancel sagas is limited. E.g. if you have only single entity you might always want to cancel the previous saga, because data from the previous response are no longer relevant.
yield takeLatest(DO_ACTION, actionSaga);
But what if you have the same action on top of multiple entities and you want to cancel the previous action only if you work with the same entity but not when it is another entity? If the list of entities is static, you can use the function pattern instead:
yield takeLatest(action => action.type === DO_ACTION && action.id = 'foo', actionSaga);
yield takeLatest(action => action.type === DO_ACTION && action.id = 'bar', actionSaga);
yield takeLatest(action => action.type === DO_ACTION && action.id = 'baz', actionSaga);
Once the list gets too long or if you have dynamic ids/amount of entities you will need to write your own logic using infinite cycles, id:task map & cancel effect.
export function* dynamicIdWatcher() {
const taskMap = {}
while (true) {
let action = yield take(DO_ACTION)
if (taskMap[action.id]) yield cancel(taskMap[action.id])
const workerTask = yield fork(actionSaga, action)
taskMap[action.id] = workerTask
}
}
Sorry for not really answering your question, but I hope some of what I wrote can still be useful in figuring out your use case :)

Do we have any lib to capture the state changes using sagas?

Basically need to build a warning modal , when user tries to move from current page/screen to another page , showing there are some saved changes .
Any implementations using redux and redux saga
Sagas are the lib for this - they watch for any action of a specified type. Navigation will take two actions: one to indicate that navigation is about to happen (which the saga will watch) and one to actually update the current page. The saga watches for actions of the first type and shows a warning dialog if the data has changed.
Ex:
function showWarning(action) {
if (/* data has been changed but not saved */) {
displayWarningDialog(action.pageToNavigateTo)
}
else {
// action that updates the page/location
completeNavigation(action.pageToNavigateTo)
}
}
function* mySaga() {
// NAVIGATE_TO_PAGE_X are the actions that get fired when a user changes pages
yield takeEvery("NAVIGATE_TO_PAGE_1", showWarning)
yield takeEvery("NAVIGATE_TO_PAGE_2", showWarning)
}
There is the amazing Redux DevTools for state debugging. This tool was built by Redux author himself.
Here are its features
Lets you inspect every state and action payload
Lets you go back in time by “cancelling” actions
If you change the reducer code, each “staged” action will be
re-evaluated
If the reducers throw, you will see during which action this
happened, and what the error was
With persistState() store enhancer, you can persist debug sessions
across page reloads
I've thought about this recently as well and been thinking about writing some form of middleware to intercept routing actions.
When intercepting a routing action, the middleware could determine if application state indicates the user is editing some unsaved data, and if so, dispatch a different action instead. That action should reduce state and cause a warning to render. The user could then confirm wanting to continue navigating by dispatching an action also intercepted by the middleware to continue the routing process.

Meteor, get all users on a specific page

We are building a chat application and are currently working on a system to see all the users in a given room.
We have a Mongo Document set up with an array of active_users where we will push and pull user names to in order to keep track of the online users. We have come to the conclusion that realizing a user has connected to a given room is fairly simple. All we need to do is in the router, when a user accesses the page, we push that user's name into the document.
Now the tricky part is realizing when that user has left that given page? Obviously jQuery isn't a reliable option, so how do we know when a user's connection to a specific page is broken?
You could do this:
Meteor.publish("page", function() {
this._session.socket.on("close", function() {
//Change your active users here
});
});
and for your page that you track
Meteor.subscribe('page');
I use this in the analytics package on atmosphere
There's an Atmosphere package called Presence that does exactly what you need.
Some extra details from the README about keeping track of custom states...
State functions
If you want to track more than just users' online state, you can set a custom state function. (The default state function returns just 'online'):
// Setup the state function on the client
Presence.state = function() {
return {
online: true,
currentRoomId: Session.get('currentRoomId')
};
}
Now we can simply query the collection to find all other users that share the same currentRoomId
Presences.find({ state: { online: true, currentRoomId: Session.get('currentRoomId') } })
Of course, presence will call your function reactively, so everyone will know as soon as things change.
Meteor has connection hooks so you can run a function when the user disconnects from the server. Setting the onClose() callback inside a method called by the client will allow you to close the userId in the function.
Code on the server could be like this:
Meteor.methods({
joinRoom: function( roomId ){
var self = this;
Rooms.update( {roomId: roomId}, {$push:{userId: self.userId}});
self.connection.onClose( function(){
Rooms.update( {roomId: roomId}, {$pull:{userId: self.userId}})
});
}
});

How can I create a reactive meteor publication with a variable momentjs date

I'm having trouble with realizing a reactive publication with a moving date. I have a calendar app with events/meetings. Im displaying these events on a special view page but i only want to display events of today and only with a starting time of currenttime - 30 mins.
The code im having and trying always works fine on initial load, after refreshing and when I add/delete an event from an admin page (collection gets refreshed?). But when I leave the page open the events which have passed just stay on the page. Whenever I add/remove a event via the admin page the publication is updated just fine. I assume this is because the publication isn't refreshing the date in the query or something?
I have tried:
normal publications and just subscribing via either iron-router
before hook or via Deps.autorun
publish all events and filtering on the client side
a publication with observeChanges
keep the vars in a deps.autorun function and passing them via the subscription as parameters
but I just keep getting the same results.
publication with observeChanges:
Meteor.publish('currentEventsObserve', function(calId) {
var self = this;
var nowMin30mins = moment().subtract('minutes',30).valueOf();
var endOfToday = moment(moment().format('DD-MM-YYYY 23:59'), 'DD-MM-YYYY HH:mm').valueOf();
var handle = Events.find({
calId : calId, //CalendarId
eventDate: { $gt: nowMin30mins, $lt: endOfToday }
},
{
sort: { eventDate: 1 },
limit: 5
}).observeChanges({
added: function(id,event){
//console.log("added: ",id,event);
self.added("events", id, event);
},
removed: function (id,event) {
//console.log("removed: ",id,event);
self.removed("events", id);
}
});
self.ready();
self.onStop(function () {
handle.stop();
});
});
As said before: the above works fine on initial load and refreshes, but after leaving the page open for a while the events are staying on there and not being removed from the publication.
Also, whenever I check the collection ones certain events already should have been removed via Events.find().fetch() for example, the events are still there, so it's not the template which isn't updating correctly.
I hope it's clear what I mean. I have read many other questions about this sort of reactivity but I just can't figure it out.
The problem is that nowMin30mins is only computed once when the publication is activated, and won't update afterwards, even though you have an observeChanges on Events. You basically have two constant values (nowMin30mins and endOfToday) bracketing the documents that will be shown and only adding or removing items will cause them to disappear.
I can think of two ways for how you'd want to do this reactively.
You can put all the reactivity on the client. Just send the entire day's events over in a publish, which is very simple, and have the client filter out what's between 30 mins from now and the end of the day using Date.now() in a computation that updates every minute or so, or when new events are added/deleted. It doesn't seem to incur a lot of overhead to store those events anyway and you can offload the computational cost of observes, which can build up if you have a lot of clients
If you want to do it on the server, you'll have to do things a little more carefully than what you have now. Because the live query won't account for the change in time, you'll need to watch all of today's events and then add or remove them from the subscription if they are created, or deleted or go out of range, respectively. To reiterate, you can't do this purely with an observeChanges because the current time is always changing.

How to know when user document loaded in Meteor Accounts

I understand that when writing code that depends on the collection being loaded into the client minimongo, that you should explicitly subscribe to the collection and pass in the appropriate callback for when it is finished loading.
My problem is that I store a lot of important subdocuments that my page needs to access in the users collection. I am using Meteor Accounts, and am trying to figure out a similar way to wait until the entire logged in user document is available. When using this to test:
console.log(Meteor.user());
the logged in case, it seems like it first registers an object with just the _id, and then sends the other fields later (I know I have to explicitly add other fields to publish from the server beyond email, etc.).
Is there a way for me to wait for the logged in user document to load completely before executing my code?
Thanks!
Deps.autorun (previously Meteor.autorun) reruns when something reactive changes, which might fit your use case:
Client js
Deps.autorun(function () {
if(Meteor.user() {
//Collection available
}
});
If you're using a subscription you can also use its callback. Have a read about it on the docs as you might have to customize it a bit, and remove the autopublish package as well as get your other collections set up to subscriptions
Server js:
Meteor.publish("userdata", function () {
//You might want to alter this depending on what you want to send down
return Meteor.users.find({}, {}});
});
Client js
Meteor.subscribe("userdata", function() {
//Collection available
});

Resources