-
Notifications
You must be signed in to change notification settings - Fork 68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
LD doesn't start if user provided later #130
Comments
Hello @janko-sokolovic, thank you for reaching out. Based on the documentation here, you will need to pass in a dummy user object to initialize the LDClient. If you don't have a user at the moment of initialization, you can follow the instruction here to use a single shared key. If you need to track the anonymous user for analytics and insights, you can follow the instruction here. |
It might be somewhat related but right now if It would be nice if we can actually observe the user variable, or at least the useFlags() hook can provide this extra info. |
@louis-launchdarkly while I understand that using an anonymous user for initialization "fixes" this, I wanted to provide a specific case where it does not appear to behave correctly. In this example, let's say that I initialized with the user, In our use case, we want The problem here is that because we're initializing with a static, anonymous user (that has no utility other than preventing quota overages and initializing the client), we get 2 flag evaluations on each page load:
In most cases, this isn't a problem, but on slow networks (or while running e2e tests), it causes bugs. Our workaround is twofold:
Ideally, we'd be able to set |
You're right, though LDClient#identify returns a promise that resolves (or you can provide a callback that is called) once it's finished loading the new user's flags - while not exactly what you're asking for, you could use that in a custom hook to know when you can hit
This is off the top of my head, so it may not work as-is... :p But you get the idea. |
Hi @louis-launchdarkly , I have the same doubt, I understand that sending some base data could solve it, but if I do this I am not clear about the functionality of the |
are there any updates on this? The documentation says:
but does not say how to define context afterwards. using |
+1 to @tlehwalder 's explanation of the core issue, having the same challenge. The help docs have an article on how to pass a deferred context into Because the HOC takes a component type and not an instance of a component / children prop, it becomes difficult to get a context later and re-render it in a component tree, like LDProvider is in the help article. Like @janko-sokolovic, I expected the client to initialize and load flags when In our use-case, we have a service dishing out anonymous user IDs (a-la Segment anonymous ID), and we don't want flag values to be evaluated before we have a known anonymous ID for this session. It would be nice to get a practical example of how |
Hello all, sorry for the late reply. I am reaching out to the React engineers on the team to double check is the Filed Internally as 212132. |
@louis-launchdarkly Any update on whether this is possible? I'm trying to rewrite our provider to use |
The LD team reached out (via support channel) and created this example a couple months ago. This uses the LDProvider export directly and gives an example of deferInitialization, without the HOC's: I was able to solve my deferInitialization troubles and get an after-first-render LD context firing without too much trouble starting with this. @lsanwick Check out this example and see if it works for what you're building! |
Hello @zachgoll, At work I was facing this exact same issue and a similar use-case you are describing. For our specific use-case, having flags initialized as anonymous users was completely irrelevant and useless, because we default flag values to High level of how we got it working well for our usecase:
Here is a stripped down version of how we made it work (had to remove lots of sensitive lines, and some of these are pseudocode, adjust based on your app). I believe this should be enough to give you the general idea of our approach. The code for the custom provider: import LaunchDarkly from "./LaunchDarkly"; // our custom wrapper class
import Loader from "./Loader";
import {useFlags, LDProvider, useLDClient, ProviderConfig } from "launchdarkly-react-client-sdk";
import React, { createContext, useContext, useState } from "react";
import { LDFlagSet } from "launchdarkly-js-sdk-common";
const FeatureFlagsContext = createContext<
| {
flags: LDFlagSet;
ldClient: LaunchDarkly;
}
| undefined
>(undefined);
// you add this provider in your App.tsx file
export function FeatureFlagsProvider(props: {
children: React.ReactNode;
providerProps?: Omit<ProviderConfig, "context">;
}) {
return (
<LDProvider {...props.providerProps} context={{ kind: "user", anonymous: true }}>
<FeatureFlagsChildren children={props.children} />
</LDProvider>
);
}
function FeatureFlagsChildren(props: { children: React.ReactNode }) {
const ldClientRaw = useLDClient();
const flags = useFlags();
const [, forceRerender] = useState(Date.now());
// first level loader - doesn't allow to render anything below the provider unless ldClientRaw get initialised with the anonymous user
if (!ldClientRaw) return <Loader />;
// this is our custom built class, it is NOT exported by launch darkly. The gist of it is that
// it wraps the ldClient, exposes its `identify` method, and we force a rerender once identify
// resolves (see next code snippet below for this class)
const ldClient = new LaunchDarkly(ldClientRaw, () => forceRerender(Date.now()));
return (
<FeatureFlagsContext.Provider value={{ flags, ldClient }}>
{props.children}
</FeatureFlagsContext.Provider>
);
}
// use this hook in your code instead of the official launch darkly exported hook `useLDClient` if
// you need to access the client. this returns the custom class instance which wraps ldClient
export function useLaunchDarklyClient() {
const data = useContext(FeatureFlagsContext);
if (!data) throw new Error("Launch Darkly client has not initialized yet");
return data.ldClient;
}
// use this hook to access flags. it handles when the client isn't initialised, and when the flags
// haven't returned yet
export function useSafeFeatureFlags() {
const data = useContext(FeatureFlagsContext);
if (!data) throw new Error("Launch Darkly client has not initialized yet");
if (!data.flags) throw new Error("Flags haven't initialised yet");
return data.flags;
} the custom class which wraps ldClient: import type {LDClient} from 'launchdarkly-js-client-sdk';
export default class LaunchDarkly {
private _client: LDClient;
private onSuccess: () => void;
constructor(lDClient: LDClient, onSuccess: () => void) {
this._client = lDClient;
this.onSuccess = onSuccess;
}
isAnonymousContext() {
return this._client.getContext()?.anonymous === true;
}
identify(user: Record<string, any>) {
this._client
.identify({
kind: 'user',
key: user.id
// ...other user attributes you care about
})
.then(() => this.onSuccess()); // this will cause the rerender if you pass a setState call in it
}
} and then finally in your App.tsx: import { FeatureFlagsProvider, useLaunchDarklyClient} from "./FeatureFlagsContext";
export default function App() {
return (
<FeatureFlagsProvider>
<FetchUserData />
</FeatureFlagsProvider>
);
}
function FetchUserData() {
const { isPending, data } = useFetchUserData();
if (isPending) return <Loader />;
return <WithFeatureFlags user={data.user} />;
}
function WithFeatureFlags(props: { user: User }) {
const ldClient = useLaunchDarklyClient();
useEffect(() => {
ldClient.identify({
id: props.user.id,
// pass in other user attributes you care about
});
}, []);
// the second level loader - important to add as well. this loader will stop showing once the
// `identify` call above resolves
if (ldClient.isAnonymousContext()) return <Loader />;
// now anything that renders after this point will have the correct flags, and will not cause a flicker.
// Be sure to use the custom hook `useSafeFeatureFlags`, and not `useFlags` which is exported from `launchdarkly-react-client-sdk`
return <ActualApp />;
} and just for clarity, this is the import React from 'react';
import {createRoot} from 'react-dom/client';
import App from './App';
createRoot(document.getElementById('root')!).render(<App />);
module.hot?.accept();
|
My team encountered the same issue. I'm frankly surprised a service of LD's reputation has such a critical issue ongoing for such a long time and that the docs recommend broken behavior:
For a simple fix,
should be changed to
I had enjoyed using LD at a previous company and and recommended it for my current team. This kind of issue really eroded a lot of trust I had built up. Please tell me there's some kind of resolution planned so I can convince other teams this is still the right solution that we should be paying for. |
Is this a support request?
This issue tracker is maintained by LaunchDarkly SDK developers and is intended for feedback on the SDK code. If you're not sure whether the problem you are having is specifically related to the SDK, or to the LaunchDarkly service overall, it may be more appropriate to contact the LaunchDarkly support team; they can help to investigate the problem and will consult the SDK team if necessary. You can submit a support request by going here or by emailing support@launchdarkly.com.
Note that issues filed on this issue tracker are publicly accessible. Do not provide any private account information on your issues. If your problem is specific to your account, you should submit a support request as described above.
Describe the bug
When initializing the LD by wrapping the App component, BUT without providing user (because user is known only after login), and then later initializing user via LDClient, no requests are sent and no flags received...
To reproduce
Initialize LD via
withLDProvider({clientSideId: 'asdasdasd'})(App)
but provide user at later stage (after login, one deeper component), viathis will not trigger flags to be loaded or are undefined
Expected behavior
Flags are loaded similarly to how it works when user is provided in initial config
Logs
n/a
SDK version
2.25.1
Language version, developer tools
JS/TS/React
OS/platform
Chromium-based browser
Additional context
Add any other context about the problem here.
The text was updated successfully, but these errors were encountered: