-
Notifications
You must be signed in to change notification settings - Fork 734
TypeError: window.heap.push is not a function. #605
Comments
@JustinTRoss did you ever figure out a way around this. I was hoping this would work:
Can't seem to get around this error |
I can't recall exactly what we did. My guess is that we ended up abandoning segment. If I'm recalling correctly, your proposal wouldn't work because segment actually rewrote the heap object somehow. |
@JustinTRoss just to be clear what do you mean by abandoning segment. Did you just get rid of using heap.io? I tried doing this as well:
But it then just doesn't load the page. I feel like maybe this is an issue with compatibility with react? I just don't get why window.heap.push doesn't exist. Been just trying anything I can think of. |
I was using Heap at the time, and I was trying out Segment. I believe I resolved to abandon Segment and just keep using Heap as I had been. I'm sure an equally appropriate option would have been to switch fully to Segment and stick to only the features they expose. Here is their instruction on Heap identification: Whatever is happening, |
@JustinTRoss so actually realized this issue and it was extremely stupid/ maybe similar to what you were doing (loading 2 libraries)- but our marketing guy added in heap to GTM so it was getting loaded twice. He added it in because we are using webflow for the main page. I had no idea it was added there as I didn't see any code in the webapp outside the script. Basically it was getting loaded twice/ causing issues. Stupid error, but maybe will help someone else if they read this |
I'm coming from google and I will try explain how this error occurs and how to avoid it. It's starting with the default Heap snippet code that can be found in Heap's documentation <script type="text/javascript">
window.heap=window.heap||[],heap.load=function(e,t){window.heap.appid=e,window.heap.config=t=t||{};var r=document.createElement("script");r.type="text/javascript",r.async=!0,r.src="https://cdn.heapanalytics.com/js/heap-"+e+".js";var a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(r,a);for(var n=function(e){return function(){heap.push([e].concat(Array.prototype.slice.call(arguments,0)))}},p=["addEventProperties","addUserProperties","clearEventProperties","identify","resetIdentity","removeEventProperty","setEventProperties","track","unsetEventProperty"],o=0;o<p.length;o++)heap[p[o]]=n(p[o])};
heap.load("YOUR_APP_ID");
</script> which can be easily translated to much more readable version below window.heap = window.heap || []
heap.load = function (appId, heapConfig) {
window.heap.appid = appId;
window.heap.config = heapConfig || {};
const script = document.createElement("script");
script.type = "text/javascript";
script.async = true;
script.src = "https://cdn.heapanalytics.com/js/heap-" + appId + ".js";
const firstScript = document.getElementsByTagName("script")[0];
firstScript.parentNode.insertBefore(script, firstScript);
const cloneArray = (arrayLike) => Array.prototype.slice.call(arrayLike, 0);
const createMethod = function (method) {
return function () {
heap.push([
method,
...cloneArray(arguments)
]);
}
};
const methods = [
'addEventProperties',
'addUserProperties',
'clearEventProperties',
'identify',
'resetIdentity',
'removeEventProperty',
'setEventProperties',
'track',
'unsetEventProperty',
];
for (let method of methods) {
heap[method] = createMethod(method)
}
};
heap.load("YOUR_APP_ID"); The problem is that the snippet from the Heap defines window.heap = window.heap || [] but when the script.src = "https://cdn.heapanalytics.com/js/heap-" + appId + ".js"; is fully loaded, The snippet also create methods that access global var n = function (e) {
return function () {
heap.push([e].concat(Array.prototype.slice.call(arguments, 0)))
}
} When this problem may occurs?When you pass function trackUser(heap, user) {
heap.identify(user.id);
}
// ...
trackUser(window.heap, authenticatedUser); especially when you do also some async function trackUser(heap, getUser) {
const user = await getUser(); // <------- Because it's async, heap script may be already loaded and now it's an object
if (user) {
// Code below will execute heap.push which throws an error...
// because global heap is the object now, not an array.
heap.identify(user.id);
}
}
// ...
trackUser(window.heap); // Here is passed an array version of window.heap from the code snippet How to fix this error?Don't pass Avoid trackUser(window.heap, authenticatedUser); Better trackUser(() => window.heap, authenticatedUser); async function trackUser(getHeap, getUser) {
const user = await getUser();
if (user) {
const heap = getHeap();
heap.identify(user.id);
}
}
// ...
trackUser(getHeap, getUser); or just use async function trackUser(getUser) {
const user = await getUser();
if (user) {
window.heap.identify(user.id);
}
}
// ...
trackUser(getUser); I hope that it will help someone. |
Thanks for the detailed explanation @hinok ! It seems like it could be a functional workaround. Ideally, Segment would just stop mutating other libs, so I'm going to leave this open in hope that a root resolution might one day be found. |
Why was this closed @pooyaj? I'm seeing the issue actively. |
@probablyup do you have a live website or a sandbox with the issue for us to debug? |
The error is in a protected part of our website, so I can't link. The reproduction though is loading the heap client via segment and then calling |
👍 Let us repro, and get back! |
@probablyup @pooyaj We don't use segment in our application but we use heap and we've had exactly the same issue. The root of the problem is the way how heap instance is created in the official code snippet before the heap script is fully loaded. The problem appears always when you pass a reference of heap before the script is loaded. Briefly looking at https://github.com/segmentio/analytics.js-integrations/blob/master/integrations/heap/lib/index.js#L35 Script is NOT loaded: it's just an array with methods You can try and recreate a demo using code from my comment #605 (comment) |
The problem I found is that heap is being called different times. This solves it. |
@Sbphillips19 - 1000 thank you's to you! I've been trying to use heap to track scrolling. Your note about heap being loaded twice because marketing added it in GTM is exactly what happened to me, i've been trying to get this to work consistently for a few hours, and the loading issue was causing it to raise the exception "heap.push is not a function" about 95% of the time. The 5% of the time it worked was really throwing me off course! Anyway, i removed the heap snippet and then was able to track scrolling. |
Just to add to this thread: One potential reason you may be seeing this error and having a hard time debugging it: The initializing script is being run somewhere else. For example, if you're adding it to Google Tag Manager and it already exists in your frontend codebase (or vice versa). To check, just run |
If loading Heap separately from Segment, when identifying with Heap (
window.heap.identify
), the page crashes with error:TypeError: window.heap.push is not a function.
While there's the obvious option of deferring all Heap usage to Segment, it seems like a bug to force the user down that path and doubly so to crash the page ambiguously.
Thoughts?
The text was updated successfully, but these errors were encountered: