Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add our own cache in front of Statsig (#3604)

authored by

dan and committed by
GitHub
41b5b5b2 02becdf4

+45 -32
+45 -32
src/lib/statsig/statsig.tsx
··· 94 94 } 95 95 } 96 96 97 + // We roll our own cache in front of Statsig because it is a singleton 98 + // and it's been difficult to get it to behave in a predictable way. 99 + // Our own cache ensures consistent evaluation within a single session. 100 + const GateCache = React.createContext<Map<string, boolean> | null>(null) 101 + 97 102 export function useGate(): (gateName: Gate) => boolean { 98 - const cache = React.useRef<Map<Gate, boolean>>() 99 - if (cache.current === undefined) { 100 - cache.current = new Map() 103 + const cache = React.useContext(GateCache) 104 + if (!cache) { 105 + throw Error('useGate() cannot be called outside StatsigProvider.') 101 106 } 102 - const gate = React.useCallback((gateName: Gate): boolean => { 103 - // TODO: Replace local cache with a proper session one. 104 - const cachedValue = cache.current!.get(gateName) 105 - if (cachedValue !== undefined) { 106 - return cachedValue 107 - } 108 - const value = Statsig.initializeCalled() 109 - ? Statsig.checkGate(gateName) 110 - : false 111 - cache.current!.set(gateName, value) 112 - return value 113 - }, []) 107 + const gate = React.useCallback( 108 + (gateName: Gate): boolean => { 109 + const cachedValue = cache.get(gateName) 110 + if (cachedValue !== undefined) { 111 + return cachedValue 112 + } 113 + const value = Statsig.initializeCalled() 114 + ? Statsig.checkGate(gateName) 115 + : false 116 + cache.set(gateName, value) 117 + return value 118 + }, 119 + [cache], 120 + ) 114 121 return gate 115 122 } 116 123 ··· 154 161 155 162 export function Provider({children}: {children: React.ReactNode}) { 156 163 const {currentAccount} = useSession() 157 - const currentStatsigUser = React.useMemo( 158 - () => toStatsigUser(currentAccount?.did), 159 - [currentAccount?.did], 160 - ) 164 + const did = currentAccount?.did 165 + const currentStatsigUser = React.useMemo(() => toStatsigUser(did), [did]) 166 + const [gateCache, setGateCache] = React.useState(() => new Map()) 167 + const [prevDid, setPrevDid] = React.useState(did) 168 + if (did !== prevDid) { 169 + setPrevDid(did) 170 + setGateCache(new Map()) 171 + } 161 172 162 173 React.useEffect(() => { 163 174 function refresh() { 164 - // Intentionally refetching the config using the JS SDK rather than React SDK 165 - // so that the new config is stored in cache but isn't used during this session. 166 - // It will kick in for the next reload. 175 + // This will not affect the current session. 176 + // Statsig will put the results into local storage and we'll pick it up on next load. 167 177 Statsig.updateUser(currentStatsigUser) 168 178 } 169 179 const id = setInterval(refresh, 3 * 60e3 /* 3 min */) ··· 171 181 }, [currentStatsigUser]) 172 182 173 183 return ( 174 - <StatsigProvider 175 - sdkKey="client-SXJakO39w9vIhl3D44u8UupyzFl4oZ2qPIkjwcvuPsV" 176 - mountKey={currentStatsigUser.userID} 177 - user={currentStatsigUser} 178 - // This isn't really blocking due to short initTimeoutMs above. 179 - // However, it ensures `isLoading` is always `false`. 180 - waitForInitialization={true} 181 - options={statsigOptions}> 182 - {children} 183 - </StatsigProvider> 184 + <GateCache.Provider value={gateCache}> 185 + <StatsigProvider 186 + key={did} 187 + sdkKey="client-SXJakO39w9vIhl3D44u8UupyzFl4oZ2qPIkjwcvuPsV" 188 + mountKey={currentStatsigUser.userID} 189 + user={currentStatsigUser} 190 + // This isn't really blocking due to short initTimeoutMs above. 191 + // However, it ensures `isLoading` is always `false`. 192 + waitForInitialization={true} 193 + options={statsigOptions}> 194 + {children} 195 + </StatsigProvider> 196 + </GateCache.Provider> 184 197 ) 185 198 }