Skip to content

useRenderReason

Saurav-TB-Pandey edited this page Aug 10, 2026 · 2 revisions

useRenderReason

A powerful, zero-overhead debugging hook that diagnoses exactly why your component just re-rendered. It catches wasted renders, deeply nested reference changes, anonymous function identities, and suspiciously frequent render loops.

Tip

This hook is zero-overhead in production. When process.env.NODE_ENV === 'production', it automatically strips its tracking logic, ensuring your app stays incredibly fast. You can leave it in your code safely.

Usage Examples

Basic

Track standard props to see why a child component is updating.

import { useRenderReason } from "react-hook-lab";

function ProductCard({ id, name, price }: { id: string, name: string, price: number }) {
  // It will print a beautiful, color-coded diagnostic log to the console
  // every time this component re-renders!
  useRenderReason("ProductCard", { id, name, price });

  return <div>{name} - ${price}</div>;
}

Common

Diagnosing function identity changes (a common cause of broken memoization).

import React, { memo } from "react";
import { useRenderReason } from "react-hook-lab";

const OptimizedButton = memo(({ onClick }: { onClick: () => void }) => {
  useRenderReason("OptimizedButton", { onClick });
  return <button onClick={onClick}>Click Me</button>;
});

function Parent() {
  // BAD! This inline function causes OptimizedButton to re-render EVERY time.
  // useRenderReason will flag this as "function-reference-changed"!
  return <OptimizedButton onClick={() => console.log("Clicked")} />;
}

Advanced

Customizing the tracking options, ignoring specific noisy props, and intercepting the output to build a custom DevTools UI.

import { useRenderReason } from "react-hook-lab";

function ComplexDashboard({ data, user, bigRef, themeContext }) {
  const info = useRenderReason("Dashboard", { data, user, bigRef }, {
    deep: true,              // Dig into object properties
    ignore: ["bigRef"],      // Don't track changes to this specific key
    warnThreshold: 5,        // Warn if it renders 5 times...
    warnWindowMs: 500,       // ...within half a second!
    logToConsole: false,     // Disable console spam
    onRender: (diagnostic) => {
      // Send the diagnostic data to an external monitoring tool
      myAnalytics.sendRenderReport(diagnostic);
    }
  });

  return <div>{/* Dashboard UI */}</div>;
}

API Reference

Parameters

  • componentName
    • Type: string
    • Description: An identifier printed in the logs to help you find the component.
  • watched
    • Type: Record<string, unknown>
    • Description: An object containing the props, state, or variables you want to track.
  • options (Optional)
    • Type: UseRenderReasonOptions
    • Description: Configuration object to adjust deep comparison, warnings, and logging.
export interface UseRenderReasonOptions {
  deep?: boolean;            // Deep-compare objects. Default: true.
  ignore?: string[];         // Keys to skip tracking.
  warnThreshold?: number;    // Render count limit. Default: 10.
  warnWindowMs?: number;     // Window limit. Default: 1000ms.
  logToConsole?: boolean;    // Auto-log to console. Default: true in dev.
  onRender?: (info: RenderReasonInfo) => void;
  trackContexts?: boolean;   // Auto-detect Context changes. Default: true.
}

Return Object

Returns RenderReasonInfo (which is also passed to onRender):

export interface RenderReasonInfo {
  componentName: string;
  renderCount: number;
  msSinceLastRender: number | null;
  changes: PropChange[];          // Array of exactly what changed and why
  isWastedRender: boolean;        // True if NOTHING changed
  isSuspiciouslyFrequent: boolean;// True if caught in a render loop
}

Core Working

useRenderReason works by caching the watched object in a useRef and executing a differential comparison on the next render phase. It categorizes changes into four distinct types:

  1. primitive-changed: A genuine value change (e.g. 1 to 2).
  2. function-reference-changed: A function changed identity (usually an un-memoized arrow function).
  3. reference-changed-value-same: An object or array changed identity, but its contents are deeply identical (usually an inline array literal [] breaking React.memo).
  4. reference-changed-value-changed: An object changed identity and its internal data actually changed.

Context Tracking: If trackContexts is true (default), the hook utilizes undocumented internal React Fiber APIs to peek at the component's dependencies.firstContext. It extracts and compares the actual React Contexts the component is subscribed to, surfacing invisible context renders without you having to manually track them!

Clone this wiki locally