launchdarkly / js-client-sdk

LaunchDarkly Client-side SDK for Browser JavaScript
Other
109 stars 62 forks source link

Reloading page causes stream error #249

Closed erlendp closed 1 year ago

erlendp commented 2 years ago

Describe the bug

I posted this on the React library github issue page (https://github.com/launchdarkly/react-client-sdk/issues/123#issue-1170305480), but looks like it may be more related to the core JS library, so reposting it here instead.

When reloading the React app, the following error is reported on the console:

Error on stream connection: {"isTrusted":true}, will continue retrying every 1000 milliseconds.

This is causing many errors to flood our logs (the error only appears once per reload of the browser page).

To reproduce

export default withLDProvider({
  clientSideID: 'xxxxxxx',
})(App);

Expected behavior

No error to be reported on reload.

Logs

[LaunchDarkly] Error on stream connection: {"isTrusted":true}, will continue retrying every 1000 milliseconds.

Version

React SDK version: 2.25.1 Browser: Chrome 99.0.4844.83 (Official Build) (x86_64) OS: Mac OSX 12.0.1

louis-launchdarkly commented 2 years ago

Hello @erlendp, thank you for reaching out, we will look at this and give you an update once we investigate the issue.

Filed Internally as 147957

kinyoklion commented 2 years ago

Hello @erlendp,

We are looking into this to determine a good way to handle it, or allowing for some configuration to modify the behavior.

We are likely not going to be use beforeunload or unload as there are some unfortunate implications of those events with modern browsers. There is a good write up on page lifecycle, which covers the issues with these events, from google: https://developer.chrome.com/blog/page-lifecycle-api/#:~:text=%23-,Legacy%20lifecycle%20APIs%20to%20avoid,-%23

The primary problem being the way in which they integrate with the backward/forward cache. Either disabling the feature, or being ignored.

So, we will be working to remove our current usage and replace them with API usage which is more friendly to modern browsers. Unfortunately browser support for events which are further into the shutdown process are either discouraged (unload, beforeunload), or they are not consistent between browsers. For instance the freeze/unfreeze events supported in Chrome would be ideal, but they do not have wide browser support.

Until then there are a few different ways that you could mitigate this in your application.

1.) If your application does not need to run when not visible, then you could close the client when the page loses visibility and then re-connect when it becomes visible. Page visibility being one of the most consistently available hooks for this type of work: https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilitychange_event#:~:text=A%20generic%20Event.-,Usage%20notes,-The%20event%20doesn%27t (This specific approach will not be taken in the SDK because some apps, for instance a music streaming app, still need to do substantial work when not visible.)

2.) If you aren't concerned with usage of unload, then you could utilize it to disconnect from the LD client.

3.) You could use the logger override in the client options to filter this log. You could also listen to one of the page lifecycle events and disable logging at a time that works for your application.

Thank you, Ryan

gautamkr04 commented 5 months ago

Hi , I have similar issue when we have long lived connection (24hr) observed that flag set to false , and re render for when flag is true. To Work again need to reload the page again.

it is very intuitive to user to observe that next day there are some issue.

const useInitialiseLDContext = () => {
    const { data: userProfile } = useAssumeGetUserProfileInfoQuery();
    let ldClient = useLDClient();
    const storedEmail = localStorage.getItem('email');

    // Initialise when client is available
    useEffect(() => {
        if (ldClient && storedEmail) {
            ldClient.identify(getLDContextObject({ email: storedEmail }));
        }
    }, [ldClient]);

    // change context when email doesn't exists , and company_group_id added.
    useEffect(() => {
        const handleVisibilityChange = () => {
            console.log(document.visibilityState, 'hook visible state');
            if (ldClient) {
                console.log('hook visible state \n', document.visibilityState, '\n hook ldClient', ldClient, '\n context    ', ldClient?.getContext());
            }
            if (document.visibilityState === 'visible' && ldClient) {
                const { email, company_group_id } = userProfile?.data || {};

                // Set email in localStorage if not already set and update to launchdarkly
                if (!storedEmail && email) {
                    localStorage.setItem('email', email);
                    ldClient.identify(getLDContextObject({ email }));
                }

                // Identify the user with the specific company_group_id if available
                if (company_group_id) {
                    const oldContext = ldClient.getContext();
                    ldClient.identify({ ...oldContext, company_group_id });
                }
            }
        };

        document.addEventListener('visibilitychange', handleVisibilityChange);

        return () => {
            document.removeEventListener('visibilitychange', handleVisibilityChange);
        };
    }, [ldClient, userProfile?.data]);
};

**we call it inside App.tsx**
<LDProvider clientSideID={environment.launchDarkly_client_SDK_key}>
     <AppRoutes />
</LDProvider>

**and inside AppRoutes call useInitialiseLDContext **
useInitialiseLDContext();

Can some one please help .