export type ChangeType = "primitive-changed" | "reference-changed-value-changed" | "reference-changed-value-same" | "function-reference-changed"; export interface PropChange { key: string; type: ChangeType; from: unknown; to: unknown; hint?: string; } export interface RenderReasonInfo { componentName: string; renderCount: number; msSinceLastRender: number | null; changes: PropChange[]; /** True when nothing tracked changed but the component still rendered. */ isWastedRender: boolean; /** True when render frequency exceeded warnThreshold within warnWindowMs. */ isSuspiciouslyFrequent: boolean; } export interface UseRenderReasonOptions { /** Deep-compare objects/arrays to distinguish reference vs value changes. Default: true. */ deep?: boolean; /** Keys to exclude from tracking (e.g. large refs, children). */ ignore?: string[]; /** Renders within warnWindowMs above this count trigger a loop warning. Default: 10. */ warnThreshold?: number; /** Sliding window size in ms for frequency detection. Default: 1000. */ warnWindowMs?: number; /** Whether to print a formatted console.group report. Default: dev-only. */ logToConsole?: boolean; /** Called on every render (after the first) with the full diagnostic info. */ onRender?: (info: RenderReasonInfo) => void; /** Automatically detect and track changes in consumed Contexts. Default: true. */ trackContexts?: boolean; } /** * useRenderReason * * Diagnoses why a component just re-rendered. Pass in whatever props/state * you want tracked, and it classifies every change: * * - primitive changed → the value genuinely changed * - reference changed, value SAME → new object/array/function identity, * but the content is identical — usually * fixable with useMemo/useCallback * - reference changed, value diff → a real change to an object/array * - function reference changed → almost always an inline arrow function * * It also flags three things most re-render debugging misses entirely: * - "wasted" renders — nothing tracked changed, so the parent likely * re-rendered unnecessarily and dragged this component with it * - Context changes — automatically peeks into the React Fiber to detect * and classify any changes in consumed Contexts (no setup required) * - "suspiciously frequent" renders — more renders than `warnThreshold` * within `warnWindowMs`, a strong signal of a re-render loop * * This hook is completely zero-overhead in production! * When `process.env.NODE_ENV === 'production'`, it automatically skips * all tracking logic unless explicitly overridden, ensuring your app stays fast. * * @example * function ProductCard({ id, name, price, onAdd }: Props) { * useRenderReason("ProductCard", { id, name, price, onAdd }); * ... * } * * @example With custom handling (e.g. feeding a dev overlay instead of console) * useRenderReason("ProductCard", { id, price }, { * logToConsole: false, * onRender: (info) => renderLog.push(info), * }); */ export declare function useRenderReason(componentName: string, watched: Record, options?: UseRenderReasonOptions): RenderReasonInfo;