launchdarkly / react-client-sdk

LaunchDarkly Client-side SDK for React.js
Other
86 stars 68 forks source link

LD double initialization with Strict Mode set to true #204

Closed Vallo closed 1 year ago

Vallo commented 1 year ago

Describe the bug In my Next.js application and only on local environment with React Strict Mode set to true, LD makes two calls to https://app.launchdarkly.com/sdk/goals/{id}. This then opens 2 websockets on https://clientstream.launchdarkly.com/eval/ and for some reason all my flags do not update, they get stuck on default values.

To reproduce Create a new Next.js app (any React 18 app should trigger it) and initialize App.tsx using

export default withLDProvider({
  clientSideID: process.env.NEXT_PUBLIC_LAUNCH_DARKLY_CLIENT_ID || "",
})(MyApp);

App.tsx renders twice, which initializes LD provider twice, triggering the bug.

Expected behavior LD should be a singleton, or there should be a way to manually call the initialize (so I can avoid calling it twice). Or it should initialize twice and handle correctly the flags updates.

SDK version 3.0.6

Language version, developer tools Nextjs 13.4.4

OS/platform Mac Pro M1

yusinto commented 1 year ago

Strict mode intentionally invokes some functions to reveal side-effects. I have tested the react sdk with a nextjs app to the best of my ability using your description and confirm that 2 network calls to clientstream are made. However flags are still updating for me so I cannot reproduce your issue. For posterity, the react sdk does not use websockets. The clientstream connections are GET http calls.

Are you able to share your source code so we can help troubleshoot please? Alternatively, please contact our support to share the code so we can help further.

Vallo commented 1 year ago

Okay I narrowed it down, this was the culprit:

    useEffect(() => {
      if (user.email) {
        getUserBillingInfo().then((userPlan: any) => {
          const ldContext = {
            kind: "user",
            key: user.email,
            email: user.email,
            firstName: user.firstname || "",
            lastName: user.lastname || "",
            currentPlanType: userPlan.type,
            currentPlan: userPlan.nickname,
          };

          ldClient?.identify(ldContext, undefined, (res) => {
            console.log("LD init callback", res);
          });
        });
        return () => {
          ldClient?.close(); // this was being called 
        };
      }
    }, [user]);

because of react strict mode, this component was being unmounted, therefore the ldClient.close() closed both connections I assume and received no updates.

should I just get rid of the ldClient.close() line? I don't think we have an scenario where we want to close the connection, other than the user closing the window.

Sorry for the wrong report! and Thanks for the quick response.

yusinto commented 1 year ago

Usually ldClient.close is called when LaunchDarkly is no longer needed to free up resources and close connections. This is usually at the end of your application life, usually when the root react component is unmounted. You want to keep ldClient alive in other scenarios, and it seems for you that this is the case.

Vallo commented 1 year ago

Closing as its no longer relevant. Thank you very much!