···9494 }
9595}
96969797+// We roll our own cache in front of Statsig because it is a singleton
9898+// and it's been difficult to get it to behave in a predictable way.
9999+// Our own cache ensures consistent evaluation within a single session.
100100+const GateCache = React.createContext<Map<string, boolean> | null>(null)
101101+97102export function useGate(): (gateName: Gate) => boolean {
9898- const cache = React.useRef<Map<Gate, boolean>>()
9999- if (cache.current === undefined) {
100100- cache.current = new Map()
103103+ const cache = React.useContext(GateCache)
104104+ if (!cache) {
105105+ throw Error('useGate() cannot be called outside StatsigProvider.')
101106 }
102102- const gate = React.useCallback((gateName: Gate): boolean => {
103103- // TODO: Replace local cache with a proper session one.
104104- const cachedValue = cache.current!.get(gateName)
105105- if (cachedValue !== undefined) {
106106- return cachedValue
107107- }
108108- const value = Statsig.initializeCalled()
109109- ? Statsig.checkGate(gateName)
110110- : false
111111- cache.current!.set(gateName, value)
112112- return value
113113- }, [])
107107+ const gate = React.useCallback(
108108+ (gateName: Gate): boolean => {
109109+ const cachedValue = cache.get(gateName)
110110+ if (cachedValue !== undefined) {
111111+ return cachedValue
112112+ }
113113+ const value = Statsig.initializeCalled()
114114+ ? Statsig.checkGate(gateName)
115115+ : false
116116+ cache.set(gateName, value)
117117+ return value
118118+ },
119119+ [cache],
120120+ )
114121 return gate
115122}
116123···154161155162export function Provider({children}: {children: React.ReactNode}) {
156163 const {currentAccount} = useSession()
157157- const currentStatsigUser = React.useMemo(
158158- () => toStatsigUser(currentAccount?.did),
159159- [currentAccount?.did],
160160- )
164164+ const did = currentAccount?.did
165165+ const currentStatsigUser = React.useMemo(() => toStatsigUser(did), [did])
166166+ const [gateCache, setGateCache] = React.useState(() => new Map())
167167+ const [prevDid, setPrevDid] = React.useState(did)
168168+ if (did !== prevDid) {
169169+ setPrevDid(did)
170170+ setGateCache(new Map())
171171+ }
161172162173 React.useEffect(() => {
163174 function refresh() {
164164- // Intentionally refetching the config using the JS SDK rather than React SDK
165165- // so that the new config is stored in cache but isn't used during this session.
166166- // It will kick in for the next reload.
175175+ // This will not affect the current session.
176176+ // Statsig will put the results into local storage and we'll pick it up on next load.
167177 Statsig.updateUser(currentStatsigUser)
168178 }
169179 const id = setInterval(refresh, 3 * 60e3 /* 3 min */)
···171181 }, [currentStatsigUser])
172182173183 return (
174174- <StatsigProvider
175175- sdkKey="client-SXJakO39w9vIhl3D44u8UupyzFl4oZ2qPIkjwcvuPsV"
176176- mountKey={currentStatsigUser.userID}
177177- user={currentStatsigUser}
178178- // This isn't really blocking due to short initTimeoutMs above.
179179- // However, it ensures `isLoading` is always `false`.
180180- waitForInitialization={true}
181181- options={statsigOptions}>
182182- {children}
183183- </StatsigProvider>
184184+ <GateCache.Provider value={gateCache}>
185185+ <StatsigProvider
186186+ key={did}
187187+ sdkKey="client-SXJakO39w9vIhl3D44u8UupyzFl4oZ2qPIkjwcvuPsV"
188188+ mountKey={currentStatsigUser.userID}
189189+ user={currentStatsigUser}
190190+ // This isn't really blocking due to short initTimeoutMs above.
191191+ // However, it ensures `isLoading` is always `false`.
192192+ waitForInitialization={true}
193193+ options={statsigOptions}>
194194+ {children}
195195+ </StatsigProvider>
196196+ </GateCache.Provider>
184197 )
185198}