UNPKG

@zendesk/react-measure-timing-hooks

Version:

react hooks for measuring time to interactive and time to render of components

103 lines (90 loc) 3.16 kB
import { useEffect, useRef } from 'react' import { useOnComponentUnmount } from '../ErrorBoundary' import { ensureTimestamp } from './ensureTimestamp' import type { BeaconConfig, UseBeacon } from './hooksTypes' import type { ComponentRenderSpan } from './spanTypes' import type { TraceManager } from './TraceManager' import type { RelationSchemasBase, RelationsOnASpan, Timestamp } from './types' type MakeEntryInput<RelationSchemasT> = Omit< ComponentRenderSpan<RelationSchemasT>, 'startTime' > & { startTime?: Timestamp } const makeEntry = <RelationSchemasT>( inp: MakeEntryInput<RelationSchemasT>, ): ComponentRenderSpan<RelationSchemasT> => ({ ...inp, startTime: ensureTimestamp(inp.startTime), }) /** * The job of the beacon: * emit component-render-start, component-render, component-unmount entries */ export const generateUseBeacon = < RelationSchemasT extends RelationSchemasBase<RelationSchemasT>, RequiredAttributesT = {}, >( traceManager: TraceManager<RelationSchemasT>, ): UseBeacon<RelationSchemasT, RequiredAttributesT> => (config: BeaconConfig<RelationSchemasT, RequiredAttributesT>) => { const renderCountRef = useRef(0) renderCountRef.current += 1 const { attributes, renderedOutput } = config const status = config.error ? 'error' : 'ok' const isIdle = config.isIdle ?? (renderedOutput === 'content' || renderedOutput === 'error') const relatedTo = config.relatedTo as unknown as RelationsOnASpan<RelationSchemasT> const renderStartTaskEntry = makeEntry<RelationSchemasT>({ ...config, relatedTo, type: 'component-render-start', duration: 0, attributes, status, renderCount: renderCountRef.current, isIdle, }) traceManager.processSpan(renderStartTaskEntry) const renderStartRef = useRef<Timestamp | undefined>() renderStartRef.current = renderStartTaskEntry.startTime // Beacon effect for tracking 'component-render'. This will fire after every render as it does not have any dependencies: useEffect(() => { traceManager.processSpan( makeEntry<RelationSchemasT>({ ...config, relatedTo, startTime: renderStartRef.current!, type: 'component-render', duration: performance.now() - renderStartRef.current!.now, status, attributes, renderCount: renderCountRef.current, isIdle, }), ) renderStartRef.current = undefined }) // Beacon effect for tracking 'component-unmount' entries useOnComponentUnmount( (errorBoundaryMetadata) => { const unmountEntry = makeEntry<RelationSchemasT>({ ...config, relatedTo, type: 'component-unmount', attributes, error: errorBoundaryMetadata?.error, errorInfo: errorBoundaryMetadata?.errorInfo, duration: 0, status: errorBoundaryMetadata?.error ? 'error' : 'ok', renderCount: renderCountRef.current, isIdle, }) traceManager.processSpan(unmountEntry) }, [config.name], ) }