Move everything out of React

This commit is contained in:
Eric Bailey
2026-01-20 11:41:13 -06:00
parent 443141d3dd
commit 5c2a799280
7 changed files with 112 additions and 163 deletions
+1 -6
View File
@@ -22,10 +22,7 @@ import {s} from '#/lib/styles'
import {ThemeProvider} from '#/lib/ThemeContext' import {ThemeProvider} from '#/lib/ThemeContext'
import I18nProvider from '#/locale/i18nProvider' import I18nProvider from '#/locale/i18nProvider'
import {logger} from '#/logger' import {logger} from '#/logger'
import { import {initializer as growthbookInitializer} from '#/logger/growthbook'
initializer as growthbookInitializer,
Provider as GrowthBookProvider,
} from '#/logger/growthbook/context'
import {Provider as A11yProvider} from '#/state/a11y' import {Provider as A11yProvider} from '#/state/a11y'
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes' import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
import {Provider as DialogStateProvider} from '#/state/dialogs' import {Provider as DialogStateProvider} from '#/state/dialogs'
@@ -147,7 +144,6 @@ function InnerApp() {
<React.Fragment <React.Fragment
// Resets the entire tree below when it changes: // Resets the entire tree below when it changes:
key={currentAccount?.did}> key={currentAccount?.did}>
<GrowthBookProvider>
<QueryProvider currentDid={currentAccount?.did}> <QueryProvider currentDid={currentAccount?.did}>
<PolicyUpdateOverlayProvider> <PolicyUpdateOverlayProvider>
<StatsigProvider> <StatsigProvider>
@@ -199,7 +195,6 @@ function InnerApp() {
</StatsigProvider> </StatsigProvider>
</PolicyUpdateOverlayProvider> </PolicyUpdateOverlayProvider>
</QueryProvider> </QueryProvider>
</GrowthBookProvider>
</React.Fragment> </React.Fragment>
</VideoVolumeProvider> </VideoVolumeProvider>
</Splash> </Splash>
+1 -6
View File
@@ -13,10 +13,7 @@ import {Provider as StatsigProvider} from '#/lib/statsig/statsig'
import {ThemeProvider} from '#/lib/ThemeContext' import {ThemeProvider} from '#/lib/ThemeContext'
import I18nProvider from '#/locale/i18nProvider' import I18nProvider from '#/locale/i18nProvider'
import {logger} from '#/logger' import {logger} from '#/logger'
import { import {initializer as growthbookInitializer} from '#/logger/growthbook'
initializer as growthbookInitializer,
Provider as GrowthBookProvider,
} from '#/logger/growthbook/context'
import {Provider as A11yProvider} from '#/state/a11y' import {Provider as A11yProvider} from '#/state/a11y'
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes' import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
import {Provider as DialogStateProvider} from '#/state/dialogs' import {Provider as DialogStateProvider} from '#/state/dialogs'
@@ -126,7 +123,6 @@ function InnerApp() {
<React.Fragment <React.Fragment
// Resets the entire tree below when it changes: // Resets the entire tree below when it changes:
key={currentAccount?.did}> key={currentAccount?.did}>
<GrowthBookProvider>
<QueryProvider currentDid={currentAccount?.did}> <QueryProvider currentDid={currentAccount?.did}>
<PolicyUpdateOverlayProvider> <PolicyUpdateOverlayProvider>
<StatsigProvider> <StatsigProvider>
@@ -174,7 +170,6 @@ function InnerApp() {
</StatsigProvider> </StatsigProvider>
</PolicyUpdateOverlayProvider> </PolicyUpdateOverlayProvider>
</QueryProvider> </QueryProvider>
</GrowthBookProvider>
</React.Fragment> </React.Fragment>
</ActiveVideoProvider> </ActiveVideoProvider>
</VideoVolumeProvider> </VideoVolumeProvider>
@@ -4,7 +4,7 @@ import {t} from '@lingui/macro'
import {DISCOVER_DEBUG_DIDS} from '#/lib/constants' import {DISCOVER_DEBUG_DIDS} from '#/lib/constants'
import {useGate} from '#/lib/statsig/statsig' import {useGate} from '#/lib/statsig/statsig'
import {logEvent} from '#/logger/growthbook/context' import {logEvent} from '#/logger/growthbook'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import * as Toast from '#/components/Toast' import * as Toast from '#/components/Toast'
@@ -1,20 +1,19 @@
import {useCallback, useEffect} from 'react' import {useCallback} from 'react'
import {Platform} from 'react-native' import {Platform} from 'react-native'
import {growthbookTrackingPlugin} from '@growthbook/growthbook/plugins' import {growthbookTrackingPlugin} from '@growthbook/growthbook/plugins'
import {GrowthBook, GrowthBookProvider} from '@growthbook/growthbook-react' import {GrowthBook} from '@growthbook/growthbook-react'
import {BSKY_SERVICE} from '#/lib/constants' import {BSKY_SERVICE} from '#/lib/constants'
import { import {
getAndMigrateStableId, getAndMigrateStableId,
getSessionId, getSessionId,
getStableId, getStableId,
getStableIdOrThrow,
} from '#/logger/growthbook/identifiers' } from '#/logger/growthbook/identifiers'
import * as referrer from '#/logger/growthbook/util/referrer' import * as referrer from '#/logger/growthbook/util/referrer'
import * as persisted from '#/state/persisted' import * as persisted from '#/state/persisted'
import {type SessionAccount, useSession} from '#/state/session' import {type SessionAccount} from '#/state/session'
import * as env from '#/env' import * as env from '#/env'
import {type Geolocation, useGeolocation} from '#/geolocation' import {device} from '#/storage'
const TIMEOUT_INIT = 500 // TODO should base on p99 or something const TIMEOUT_INIT = 500 // TODO should base on p99 or something
const TIMEOUT_PREFER_LOW_LATENCY = 250 const TIMEOUT_PREFER_LOW_LATENCY = 250
@@ -63,18 +62,12 @@ type UserAttributes = GrowthBookDefaultUserAttributes & {
} }
type Attributes = DefaultAttributes & UserAttributes type Attributes = DefaultAttributes & UserAttributes
/**
* We cache the geolocation outside of React to use when setting
* default attributes outside React, such as during `refresh()`.
*/
let unsafeGeolocation: Geolocation | null = null
const gb = new GrowthBook({ const gb = new GrowthBook({
apiHost: env.GROWTHBOOK_API_HOST, apiHost: env.GROWTHBOOK_API_HOST,
clientKey: env.GROWTHBOOK_CLIENT_KEY, clientKey: env.GROWTHBOOK_CLIENT_KEY,
plugins: [growthbookTrackingPlugin()], plugins: [growthbookTrackingPlugin()],
trackingCallback: (experiment, result) => { trackingCallback: (experiment, result) => {
console.log('Experiment Viewed', { console.debug('Experiment Viewed', {
experimentId: experiment.key, experimentId: experiment.key,
variationId: result.key, variationId: result.key,
}) })
@@ -83,10 +76,7 @@ const gb = new GrowthBook({
variationId: result.key, variationId: result.key,
}) })
}, },
attributes: { attributes: getDefaultAttributes(),
device_id: getStableId() || 'unset',
session_id: getSessionId(),
} satisfies GrowthBookDefaultAttributes,
}) })
/** /**
@@ -147,47 +137,15 @@ export function useGate() {
}, []) }, [])
} }
/**
* Main provider for GrowthBook and feature flag context. Should be rendered
* _after_ `initializer` is complete, and _after_ the Session provider.
*/
export function Provider({children}: {children: React.ReactNode}) {
const geo = useGeolocation()
const {currentAccount} = useSession()
/**
* Decorate existing attributes with any new default attributes
*/
useEffect(() => {
// cache outside react
unsafeGeolocation = geo
const attr = {
...gb.getAttributes(),
...getDefaultAttributes(),
}
gb.setAttributes(attr)
console.debug(`update attributes`, {attributes: attr})
}, [geo])
/**
* Update user attributes on session change, and clear them on logout
*/
useEffect(() => {
setAttributesForAccount(currentAccount)
}, [currentAccount])
return <GrowthBookProvider growthbook={gb}>{children}</GrowthBookProvider>
}
/** /**
* Get the default attributes that should always be set * Get the default attributes that should always be set
* on the GrowthBook instance * on the GrowthBook instance
*/ */
function getDefaultAttributes() { function getDefaultAttributes() {
return { return {
device_id: getStableIdOrThrow(), device_id: getStableId() || 'unset',
session_id: getSessionId(), session_id: getSessionId(),
country: unsafeGeolocation?.countryCode || 'unknown', country: device.get(['mergedGeolocation'])?.countryCode || 'unknown',
} }
} }
+1 -1
View File
@@ -1,6 +1,6 @@
import {nanoid} from 'nanoid/non-secure' import {nanoid} from 'nanoid/non-secure'
import {logEvent} from '#/lib/statsig/statsig' import {logEvent} from '#/logger/growthbook'
import {add} from '#/logger/logDump' import {add} from '#/logger/logDump'
import {type MetricEvents} from '#/logger/metrics' import {type MetricEvents} from '#/logger/metrics'
import {consoleTransport} from '#/logger/transports/console' import {consoleTransport} from '#/logger/transports/console'
+1
View File
@@ -15,6 +15,7 @@ export enum LogContext {
AgeAssurance = 'age-assurance', AgeAssurance = 'age-assurance',
PolicyUpdate = 'policy-update', PolicyUpdate = 'policy-update',
Geolocation = 'geolocation', Geolocation = 'geolocation',
GrowthBook = 'growthbook',
/** /**
* METRIC IS FOR INTERNAL USE ONLY, don't create any other loggers using this * METRIC IS FOR INTERNAL USE ONLY, don't create any other loggers using this
+1 -1
View File
@@ -24,7 +24,7 @@ import {
} from '#/lib/constants' } from '#/lib/constants'
import {getAge} from '#/lib/strings/time' import {getAge} from '#/lib/strings/time'
import {logger} from '#/logger' import {logger} from '#/logger'
import {refresh as refreshGates} from '#/logger/growthbook/context' import {refresh as refreshGates} from '#/logger/growthbook'
import {snoozeBirthdateUpdateAllowedForDid} from '#/state/birthdate' import {snoozeBirthdateUpdateAllowedForDid} from '#/state/birthdate'
import {snoozeEmailConfirmationPrompt} from '#/state/shell/reminders' import {snoozeEmailConfirmationPrompt} from '#/state/shell/reminders'
import { import {