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 | 34x 34x 34x 34x 34x 34x 34x 34x 18x 18x 19x 18x 19x 19x 19x 16x 14x 3x 2x 2x 19x 15x 18x 18x 2x 1x 18x 18x 18x 18x 34x 2x 8x 2x 4x 2x 4x 2x 4x | 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>,
);
};
|