braze-inc / braze-web-sdk

Public repo for the Braze Web SDK
https://www.braze.com
Other
70 stars 25 forks source link

[Question]: How to wait in a website for window.braze when it's loaded and initialized from GTM #158

Closed ilgianfra closed 6 hours ago

ilgianfra commented 1 week ago

What problem are you facing?

Hi,

This is more a question/ doubt we are facing in our project: We are loading and initializing braze with GTM so in the website we are kind waiting for window.braze in order to call getCachedContentCards and some other methods (already here it looked weird to me).

But sometimes we get: Error: Braze must be initialized before calling methods. so I suppose that in our web we have available window.braze but the initialization is not over yet, some kind of race condition 🤔

So my question is that the way we are doing is correct.

Thanks!

Workarounds

Maybe we should load braze in our web and not in GTM?

Ideal Solution

No response

Other Information

No response

davidbielik commented 1 week ago

Hi @ilgianfra you can also set up your initialization tag as a "setup tag" so you guarantee initialization prior to your custom code.

Let me know if this doesn't help, please provide more details on your specific sequence of tags.

ilgianfra commented 1 week ago

We are using custom code outside of GTM, I mean we have a react app with GTM. Inside of GTM we load Braze and then in our react app we do something like if(window.braze){...do stuff..}

So it looks like that we have available window.braze but not the initialization.

davidbielik commented 1 week ago

That makes sense, and unfortunately when mixing GTM and your own application code you are likely to run into a race condition since GTM doesn't have the ability to register callbacks for each tag.

A few possible workarounds:

  1. Switch your Braze installation from GTM to NPM.

If you still want to use GTM you could expose braze globally, but then you'd lose out on tree-shaking performance benefits our NPM package provides:

import * as braze from "@braze/web-sdk"
window.braze = braze
  1. Set up a custom waitForBraze function in your code:
export function waitForBraze(timeout: number): Promise<void> {
    return new Promise((resolve, reject) => {
        const intervalTime = 100; // Check every 100 milliseconds
        let elapsedTime = 0;

        const interval = setInterval(() => {
            elapsedTime += intervalTime;

            if (window.braze) {
                clearInterval(interval);
                resolve(window.braze);
            } else if (elapsedTime >= timeout) {
                clearInterval(interval);
                reject(new Error('window.braze not found within the specified timeout'));
            }
        }, intervalTime);
    });
}

It can be used like so:

waitForBraze(3000).then((braze) => {
   // use braze functions safely
})
.catch(() => {
   // GTM didn't load in time
});
ilgianfra commented 6 days ago

Thanks!

We are using a similar approach:

  useEffect(() => {
    if (!isBrazeLoaded) {
      const interval = setInterval(() => {
        timesRun += 1;
        if (timesRun === 100) {
          clearInterval(interval);
        }
        if (window.braze && !isBrazeLoaded) {
          setIsBrazeLoaded(true);
          clearInterval(interval);
        }
      }, 100);
    }
  }, [isBrazeLoaded, setIsBrazeLoaded]);

That's why I get Error: Braze must be initialized before calling methods. because I think that Braze is available but not yet initialized 🤔 In my code I check isBrazeLoaded variable

ilgianfra commented 6 days ago

I did some test with this code and I get the same error also with the Promise approach. I think we can't avoid this race condition 🤔

davidbielik commented 6 days ago

Hi @ilgianfra I understand now, since the GTM integration just downloads the library it doesn't pre-create the SDK methods to enqueue prior to initialization like our CDN snippet includes.

As a workaround, instead of checking for window.braze to exist in your hook, you can check for typeof window.braze?.getDeviceId() !== 'undefined'. Once the SDK initializes, the device ID will no longer be undefined.

Our team will look into this as a future improvement, perhaps some new property you can check more easily (i.e. braze.isInitialized() - open to suggestions if you have any).

ilgianfra commented 5 days ago

Thanks! I am going to test this typeof window.braze.getDeviceId() !== 'undefined' and see if it will fix my issue 🤚

Yeap, it could be a good idea to have braze.isInitialized() do you want me to open a new ticket with that suggestion? or maybe this internal config that we have is kind of an edge case so it is not worth it?

davidbielik commented 1 day ago

Hi @ilgianfra did this updated logic fix your issue?

ilgianfra commented 10 hours ago

Hi @davidbielik !

Yes it did! we updated the version and used typeof window.braze.getDeviceId() !== 'undefined'.

thank you very much for your help and support!

Should I keep this open for the future request of braze.isInitialized()?

davidbielik commented 6 hours ago

Glad to hear it! We don't have a timeline for when this will be prioritized but will comment here when it's been released.