Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | 38x 38x 38x 38x 38x 38x 38x 38x 20x 20x 21x 20x 21x 21x 21x 18x 16x 3x 2x 2x 21x 17x 20x 20x 2x 1x 20x 20x 20x 20x 38x 2x 4x 2x 4x 2x 4x 2x 12x | import { useEffect, useRef, useState } from "react";
import { launchdarklyService } from "@repo/services/launchdarkly";
type UseLaunchDarklyResult<T> = {
value: T;
loading: boolean;
error: Error | null;
};
type VariationMethod<T> = (flagKey: string, defaultValue: T) => Promise<T>;
/**
* Generic hook for LaunchDarkly feature flags with real-time updates
*/
export function useLaunchDarkly<T>(
flagKey: string,
defaultValue: T,
variationMethod: VariationMethod<T>,
): UseLaunchDarklyResult<T> {
const [value, setValue] = useState<T>(defaultValue);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<Error | null>(null);
// Store variation method in ref to avoid dependency issues
const variationMethodRef = useRef(variationMethod);
variationMethodRef.current = variationMethod;
// Store defaultValue in ref to avoid stale closures
const defaultValueRef = useRef(defaultValue);
defaultValueRef.current = defaultValue;
useEffect(() => {
let isMounted = true;
const updateFlagValue = async (setLoadingState: boolean) => {
if (setLoadingState && isMounted) {
setLoading(true);
}
try {
setError(null);
const flagValue = await variationMethodRef.current(
flagKey,
defaultValueRef.current,
);
if (isMounted) {
setValue(flagValue);
}
} catch (err) {
if (isMounted) {
setError(err instanceof Error ? err : new Error(String(err)));
setValue(defaultValueRef.current);
}
} finally {
if (setLoadingState && isMounted) {
setLoading(false);
}
}
};
// Fetch initial value
updateFlagValue(true);
// Subscribe to real-time flag changes
// Use a stable handler function that always has access to latest state
const handler = () => {
if (isMounted) {
updateFlagValue(false);
}
};
const unsubscribe = launchdarklyService.onFlagChange(flagKey, handler);
return () => {
isMounted = false;
unsubscribe();
};
}, [flagKey]);
return { value, loading, error };
}
export const useLaunchDarklyBool = (
flagKey: string,
defaultValue: boolean = false,
): UseLaunchDarklyResult<boolean> => {
return useLaunchDarkly(
flagKey,
defaultValue,
launchdarklyService.boolVariation.bind(launchdarklyService),
);
};
export const useLaunchDarklyString = (
flagKey: string,
defaultValue: string = "",
): UseLaunchDarklyResult<string> => {
return useLaunchDarkly(
flagKey,
defaultValue,
launchdarklyService.stringVariation.bind(launchdarklyService),
);
};
export const useLaunchDarklyNumber = (
flagKey: string,
defaultValue: number = 0,
): UseLaunchDarklyResult<number> => {
return useLaunchDarkly(
flagKey,
defaultValue,
launchdarklyService.numberVariation.bind(launchdarklyService),
);
};
export const useLaunchDarklyJson = <T = unknown>(
flagKey: string,
defaultValue: T,
): UseLaunchDarklyResult<T> => {
return useLaunchDarkly(
flagKey,
defaultValue,
launchdarklyService.jsonVariation.bind(
launchdarklyService,
) as VariationMethod<T>,
);
};
|