packages/render-spin-check/src/hooks/useRenderSpinCheck.tsx
import { useEffect, useMemo, useState } from 'react'
export interface RenderSpinCheckConfig {
maxRate?: number
minSamples?: number
noThrow?: boolean
reportOnce?: boolean
}
export interface RenderSpinCheckBounce {
name: string
}
//this is external to prevent us from creating our own spin
const spinCountMap = new Map<number, number>()
export const useRenderSpinCheck = (bounce: RenderSpinCheckBounce, config?: RenderSpinCheckConfig) => {
const startTime = useMemo(() => Date.now(), [])
const [error, setError] = useState<Error>()
useEffect(() => {
if (error) {
if (!config?.reportOnce) {
console.warn(error.message)
}
} else {
const spinCount = spinCountMap.get(startTime) ?? 0
if (spinCount > (config?.minSamples ?? 20)) {
const elapsedTime = Date.now() - startTime
const refreshRate = elapsedTime / spinCount
if (refreshRate < (config?.maxRate ?? 1000)) {
const error = Error(`Spinning [${bounce.name}] [Rate=${refreshRate.toFixed(2)}ms, Samples=${spinCount}]`)
console.warn(error.message)
setError(error)
if (!config?.noThrow) {
throw error
}
}
}
spinCountMap.set(startTime, spinCount + 1)
}
return () => {
spinCountMap.delete(startTime)
}
}, [bounce, config, error, startTime])
}