bulky-flower-61836
05/08/2025, 3:38 PMuseFeatureValue
hook allows specifying a fallback value, but this fallback is returned even if the flag doesn't exist or has been deleted for a given environment or user—making it unreliable.
React Native: The useFeatureValue
hook briefly returns the fallback value, then switches to false, and only after a delay resolves to the actual flag value. This further exacerbates the flicker.
Overall, the current approach makes it difficult to trust the feature flag's initial state, especially during the resolution phase.
Expected Behavior:
A loading state should be provided while the feature flag value is being resolved. This would enable developers to render a fallback UI (e.g., spinner or skeleton) and avoid rendering any feature version until the flag value is fully determined.
Current Behavior:
There is no mechanism to detect if the feature flag is still resolving. As a result, the UI often renders the default version prematurely, leading to a visible flicker when the correct version replaces it.
Suggested Solution:
Expose a loading or isResolving state from the feature flag hook/provider. This would allow developers to wait until the flag value is fully resolved before rendering either component version, avoiding unexpected flickers and improving UX.strong-mouse-55694
05/09/2025, 10:07 PMbulky-flower-61836
05/12/2025, 8:14 AMstrong-mouse-55694
05/12/2025, 2:56 PMconst gb = useGrowthBook();
if (gb.ready) {
// Do something
}
bulky-flower-61836
05/13/2025, 8:36 AMstrong-mouse-55694
05/13/2025, 1:45 PMbulky-flower-61836
05/13/2025, 1:50 PMtrue
in any environment, they still take time to resolve.
For now, we've implemented a workaround: we check any flag only after resolving an attribute-based flag (like feature_loaded = true
). But this is just a temporary workaround.strong-mouse-55694
05/13/2025, 1:57 PMnull
which triggers the fallback value.bulky-flower-61836
05/14/2025, 7:41 AMstrong-mouse-55694
05/14/2025, 2:24 PMbulky-flower-61836
05/15/2025, 8:25 AMconst [gb] = useState(() => {
return new GrowthBook({
enableDevMode: import.meta.env.DEV,
clientKey: import.meta.env.VITE_GROWTHBOOK_CLIENT_KEY,
decryptionKey: import.meta.env.VITE_GROWTHBOOK_DECRYPTION_KEY,
});
});
• then we initialize the growthbook after we get growthbook config from applicaiton API.
useEffect(() => {
if (growthbookConfig) {
gb.init({ payload: growthbookConfig, streaming: false });
}
}, [growthbookConfig]);
• We set attributes and config on useEffect when we get values
useEffect(() => {
if (appSettings && appSettings?.growthbookConfig) {
setgrowthbookConfig(appSettings?.growthbookConfig);
}
}, [appSettings]);
useEffect(() => {
if (isAuthLoaded && growthbookConfig) {
if(isAuthenticated && areUserSettingsLoaded) return;
gb.setAttributes({
applicationId,
id: onlyUserId,
guestUserId,
});
}
}, [
growthbookConfig,
isAuthLoaded,
isAuthenticated,
areUserSettingsLoaded,
onlyUserId,
]);
Then we just pass that instance on provider and use it through hook on different places. We show loader on application level until we get response from our application api for growhtbook and other configurations.