All files / src use-visitor-data.ts

95.65% Statements 22/23
100% Branches 8/8
80% Functions 4/5
95.65% Lines 22/23

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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117                                                                                    37x   37x   37x 37x             37x   13x   13x 13x             13x         13x 12x             12x   1x   1x             1x           37x 16x 6x           37x 2x     37x 34x              
import { FingerprintContext, FingerprintContextInterface, VisitorQueryResult } from './fingerprint-context'
import { useCallback, useContext, useEffect, useMemo, useState } from 'react'
import deepEquals from 'fast-deep-equal'
import { toError } from './utils/to-error'
import { assertIsTruthy } from './utils/assert-is-truthy'
import { GetOptions, GetResult } from '@fingerprint/agent'
 
export interface UseVisitorDataConfig {
  /**
   * Determines whether the `getData()` method will be called immediately after the hook mounts or not
   */
  immediate: boolean
}
 
export type UseVisitorDataOptions = GetOptions & UseVisitorDataConfig
 
export type UseVisitorDataReturn = VisitorQueryResult & {
  /**
   * Performs identification request to server and returns visitors data.
   * */
  getData: (getDataOptions?: GetOptions) => Promise<GetResult>
}
 
/**
 *  @example
 * ```js
 *  const {
 *    // Request state
 *    data,
 *    isLoading,
 *    error,
 *    // A method to be called manually when the `immediate` field in the config is set to `false`:
 *    getData,
 *  } = useVisitorData({ extended: true, immediate: false });
 * ```
 * Use the `useVisitorData` hook in your components to perform identification requests with the Fingerprint API. The returned object contains information about loading status, errors, and visitor.
 *
 * @param {UseVisitorDataOptions} options for the `agent.get()` request and for hook
 */
export function useVisitorData(
  { immediate, ...getOptions }: UseVisitorDataOptions = { immediate: true }
): UseVisitorDataReturn {
  assertIsTruthy(getOptions, 'getOptions')
 
  const { getVisitorData } = useContext<FingerprintContextInterface>(FingerprintContext)
 
  const [currentGetOptions, setCurrentGetOptions] = useState(getOptions)
  const [queryState, setQueryState] = useState<VisitorQueryResult>({
    isLoading: immediate,
    data: undefined,
    isFetched: false,
    error: undefined,
  })
 
  const getData = useCallback<UseVisitorDataReturn['getData']>(
    async (params = {}) => {
      assertIsTruthy(params, 'getDataParams')
 
      try {
        setQueryState({
          isLoading: true,
          isFetched: false,
          data: undefined,
          error: undefined,
        })
 
        const getDataOptions: GetOptions = {
          ...currentGetOptions,
          ...params,
        }
 
        const result = await getVisitorData(getDataOptions)
        setQueryState({
          isLoading: false,
          isFetched: true,
          data: result,
          error: undefined,
        })
 
        return result
      } catch (unknownError) {
        const error = toError(unknownError)
 
        setQueryState({
          isLoading: false,
          isFetched: false,
          data: undefined,
          error,
        })
 
        throw error
      }
    },
    [currentGetOptions, getVisitorData]
  )
 
  useEffect(() => {
    if (immediate) {
      getData().catch((error) => {
        console.error(`Failed to fetch visitor data on mount: ${error}`)
      })
    }
  }, [immediate, getData])
 
  if (!Object.is(currentGetOptions, getOptions) && !deepEquals(currentGetOptions, getOptions)) {
    setCurrentGetOptions(getOptions)
  }
 
  return useMemo(
    () => ({
      ...queryState,
      getData,
    }),
    [queryState, getData]
  )
}