All files / src FingerprintProvider.tsx

100% Statements 17/17
100% Branches 6/6
100% Functions 6/6
100% Lines 14/14

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                                                        35x         35x 35x 2x     35x   35x 35x 24x 22x   2x       35x   20x         35x   35x    
import React, { PropsWithChildren, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { start } from './FingerprintClient'
import { FingerprintContext } from './FingerprintContext'
import type { GetOptions, StartOptions } from './types'
import { deepEqual } from './utils'
import { usePromiseStore } from './usePromiseStore'
 
/**
 * Provides the {@link FingerprintContext} to its child components.
 *
 * Applications should create only one client: either through a single `FingerprintProvider`
 * at the root of the component tree or through the API client (`start()`). Creating another
 * client will overwrite the underlying native client of the previous one.
 *
 * @group Hooks approach
 *
 * @example
 * ```jsx
 * <FingerprintProvider
 *   apiKey="your-fpjs-public-api-key"
 *   region="eu"
 *   android={{ locationTimeoutMillis: 5000 }}
 * >
 *   <MyApp />
 * </FingerprintProvider>
 * ```
 */
export function FingerprintProvider({ children, ...options }: PropsWithChildren<StartOptions>) {
  const { doRequest } = usePromiseStore()
 
  // `options` is a fresh object on every render (rest spread), so we cannot depend on its identity.
  // Keep a stable reference that only changes when the options change by value. This also spares
  // consumers from having to memoize inline object props (e.g. `android`, `web`).
  const [stableOptions, setStableOptions] = useState(options)
  if (!Object.is(stableOptions, options) && !deepEqual(stableOptions, options)) {
    setStableOptions(options)
  }
 
  const [client, setClient] = useState(() => start(stableOptions))
 
  const firstRenderRef = useRef(true)
  useEffect(() => {
    if (firstRenderRef.current) {
      firstRenderRef.current = false
    } else {
      setClient(start(stableOptions))
    }
  }, [stableOptions])
 
  const getVisitorData = useCallback(
    (getOptions?: GetOptions) => {
      return doRequest(() => client.get(getOptions), getOptions)
    },
    [client, doRequest]
  )
 
  const contextValue = useMemo(() => ({ client, getVisitorData }), [client, getVisitorData])
 
  return <FingerprintContext.Provider value={contextValue}>{children}</FingerprintContext.Provider>
}