Skip to content

Commit

Permalink
fix: correctly provide contextual information when provider is mounted
Browse files Browse the repository at this point in the history
  • Loading branch information
TheUnderScorer committed Jul 15, 2022
1 parent baa5605 commit 5d8f195
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 45 deletions.
2 changes: 1 addition & 1 deletion __tests__/fpjs-provider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe(`FpjsProvider`, () => {
expect.objectContaining({
loadOptions: expect.objectContaining({
...loadOptions,
integrationInfo: [`fingerprintjs-pro-react/${packageInfo.version}`],
integrationInfo: [`fingerprintjs-pro-react/${packageInfo.version}/react`],
}),
cacheLocation: CacheLocation.LocalStorage,
cachePrefix: 'TEST_PREFIX',
Expand Down
14 changes: 4 additions & 10 deletions examples/next/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,6 @@ next@12.1.5:
"@next/swc-win32-ia32-msvc" "12.1.5"
"@next/swc-win32-x64-msvc" "12.1.5"

object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=

picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
Expand All @@ -194,13 +189,12 @@ postcss@8.4.5:
version "0.0.0"
uid ""

scheduler@^0.20.2:
version "0.20.2"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
scheduler@^0.23.0:
version "0.23.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"

source-map-js@^1.0.1:
version "1.0.2"
Expand Down
93 changes: 59 additions & 34 deletions src/components/fpjs-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { PropsWithChildren, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import FpjsContext from '../fpjs-context'
import { Agent, FpjsClient, FpjsClientOptions } from '@fingerprintjs/fingerprintjs-pro-spa'
import { FpjsClient, FpjsClientOptions } from '@fingerprintjs/fingerprintjs-pro-spa'
import * as packageInfo from '../../package.json'
import { agentServerMock } from '../agent-server-mock'
import { isSSR } from '../ssr'
import { getEnvironment } from '../get-env'
import { type DetectEnvContext } from '../detect-env'
import type { EnvDetails } from '../env.types'
import { SyntheticEventDetector } from './synthetic-event-detector'
import { waitUntil } from '../utils/wait-until'

const pkgName = packageInfo.name.split('/')[1]

Expand Down Expand Up @@ -45,10 +45,22 @@ export function FpjsProvider<TExtended extends boolean>({
loadOptions,
}: PropsWithChildren<FpjsProviderOptions>) {
const [env, setEnv] = useState<EnvDetails | undefined>()

const [envContext, setEnvContext] = useState<DetectEnvContext | undefined>()

const clientInitPromiseRef = useRef<Promise<unknown>>()
const clientRef = useRef<FpjsClient>()

const clientOptions = useMemo(() => {
return {
cache,
cacheTimeInSeconds,
cachePrefix,
cacheLocation,
loadOptions,
}
}, [loadOptions, cache, cacheTimeInSeconds, cachePrefix, cacheLocation])

const createClient = useCallback(() => {
let integrationInfo = `${pkgName}/${packageInfo.version}`

if (env) {
Expand All @@ -57,51 +69,50 @@ export function FpjsProvider<TExtended extends boolean>({
integrationInfo += `/${envInfo}`
}

return {
cache,
cacheTimeInSeconds,
cachePrefix,
cacheLocation,
const parsedClientOptions = {
...clientOptions,
loadOptions: {
...loadOptions,
integrationInfo: [...(loadOptions.integrationInfo || []), integrationInfo],
},
}
}, [loadOptions, env, cache, cacheTimeInSeconds, cachePrefix, cacheLocation])

const [client, setClient] = useState<FpjsClient>(() => new FpjsClient(clientOptions))
const createdClient = new FpjsClient(parsedClientOptions)

useEffect(() => {
if (forceRebuild) {
setClient(new FpjsClient(clientOptions))
clientInitPromiseRef.current = createdClient.init()

clientRef.current = createdClient
}, [clientOptions, env, loadOptions])

const getClient = useCallback(async () => {
if (isSSR()) {
throw new Error('FpjsProvider client cannot be used in SSR')
}
}, [forceRebuild, clientOptions])

const clientPromise = useRef<Promise<Agent>>(
new Promise((resolve) => {
if (!isSSR()) {
resolve(client.init())
} else {
resolve(agentServerMock)
}
})
)
const firstRender = useRef(true)

useEffect(() => {
if (firstRender) {
firstRender.current = false
} else {
clientPromise.current = client.init()
if (!clientRef.current) {
await waitUntil({
checkCondition: () => Boolean(clientRef.current),
}).catch(async () => {
/**
* We did timeout waiting for ideal condition to create client (eg: env detection)
* Attempt to create client now, potentially without some additional information that might be useful but are not required.
* */
createClient()
})
}
}, [client])

return clientRef.current!
}, [createClient])

const getVisitorData = useCallback(
async (options, ignoreCache) => {
await clientPromise.current
const client = await getClient()

await clientInitPromiseRef.current

return client.getVisitorData<TExtended>(options, ignoreCache)
},
[client]
[getClient]
)

const handleSyntheticEventResult = useCallback((isSyntheticEvent: boolean) => {
Expand All @@ -115,8 +126,10 @@ export function FpjsProvider<TExtended extends boolean>({
}, [])

const clearCache = useCallback(async () => {
const client = await getClient()

await client.clearCache()
}, [client])
}, [getClient])

const contextValue = useMemo(() => {
return {
Expand All @@ -125,6 +138,18 @@ export function FpjsProvider<TExtended extends boolean>({
}
}, [clearCache, getVisitorData])

useEffect(() => {
if (env) {
createClient()
}
}, [env, createClient])

useEffect(() => {
if (forceRebuild) {
createClient()
}
}, [forceRebuild, clientOptions, createClient])

return (
<FpjsContext.Provider value={contextValue}>
{!envContext && <SyntheticEventDetector onResult={handleSyntheticEventResult} />}
Expand Down
23 changes: 23 additions & 0 deletions src/utils/wait-until.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export interface WaitUntilParams {
checkCondition: () => boolean
timeoutMs?: number
intervalMs?: number
}

export function waitUntil({ checkCondition, intervalMs = 250, timeoutMs = 2000 }: WaitUntilParams) {
return new Promise<void>((resolve, reject) => {
const timeoutId = setTimeout(() => {
clearInterval(interval)

reject(new Error('Timeout'))
}, timeoutMs)

const interval = setInterval(() => {
if (checkCondition()) {
clearTimeout(timeoutId)
clearInterval(interval)
resolve()
}
}, intervalMs)
})
}

0 comments on commit 5d8f195

Please sign in to comment.