CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-welldone-software--why-did-you-render

Monkey patches React to notify about avoidable re-renders by tracking pure components and hooks.

Pending
Overview
Eval results
Files

component-tracking.mddocs/

Component Tracking and Notifications

Built-in component tracking and notification system that provides detailed analysis of re-render causes and patterns.

Capabilities

Update Information Structure

Complete information about a component re-render event, provided to notifiers:

interface UpdateInfo {
  /** The component that re-rendered */
  Component: React.Component;
  
  /** Display name of the component */
  displayName: string;
  
  /** Previous props object */
  prevProps: any;
  
  /** Previous state object */
  prevState: any;
  
  /** Next props object */
  nextProps: any;
  
  /** Next state object */
  nextState: any;
  
  /** Previous hook result (for hook updates) */
  prevHookResult: any;
  
  /** Next hook result (for hook updates) */
  nextHookResult: any;
  
  /** Detailed reason for the update */
  reason: ReasonForUpdate;
  
  /** WDYR options used for this tracking */
  options: WhyDidYouRenderOptions;
  
  /** Name of the hook (if this is a hook update) */
  hookName?: string;
  
  /** Previous owner component (for owner tracking) */
  prevOwner?: React.Component;
  
  /** Next owner component (for owner tracking) */
  nextOwner?: React.Component;
}

Re-render Reason Analysis

Detailed analysis of why a component re-rendered:

interface ReasonForUpdate {
  /** Array of hook-related changes that caused the re-render */
  hookDifferences: HookDifference[];
  
  /** Whether props changes contributed to the re-render */
  propsDifferences: boolean;
  
  /** Whether state changes contributed to the re-render */
  stateDifferences: boolean;
  
  /** Owner component differences (when logOwnerReasons is enabled) */
  ownerDifferences?: {
    propsDifferences?: HookDifference[];
    stateDifferences?: HookDifference[];
    hookDifferences?: Array<{
      hookName: string;
      differences: HookDifference[];
    }>;
  };
}

interface HookDifference {
  /** Path string to the changed value (e.g., "0" for useState, "items[2].name" for nested) */
  pathString: string;
  
  /** Type of difference detected (e.g., "different", "deepEquals") */
  diffType: string;
  
  /** Previous value before the change */
  prevValue: any;
  
  /** Next value after the change */
  nextValue: any;
}

Default Notifier

Built-in notification handler that logs re-render information to the console:

/**
 * Default notification handler for component re-render events
 * Logs detailed information about why the component re-rendered
 * @param updateInfo - Complete information about the component update
 */
function defaultNotifier(updateInfo: UpdateInfo): void;

Usage Example:

import whyDidYouRender from '@welldone-software/why-did-you-render';

// The default notifier is used automatically
whyDidYouRender(React, {
  trackAllPureComponents: true
});

// Access the default notifier directly if needed
console.log(whyDidYouRender.defaultNotifier);

Custom Notifiers

Create custom notification handlers for specialized logging or integration with monitoring systems:

/**
 * Custom notifier function type
 * @param updateInfo - Information about the component update
 */
type Notifier = (updateInfo: UpdateInfo) => void;

Usage Examples:

// Custom notifier that sends data to analytics
const analyticsNotifier = (updateInfo) => {
  analytics.track('component_rerender', {
    componentName: updateInfo.displayName,
    hasPropsChanges: updateInfo.reason.propsDifferences,
    hasStateChanges: updateInfo.reason.stateDifferences,
    hookChanges: updateInfo.reason.hookDifferences.length
  });
};

whyDidYouRender(React, {
  trackAllPureComponents: true,
  notifier: analyticsNotifier
});

// Custom notifier that filters by component type
const filteredNotifier = (updateInfo) => {
  if (updateInfo.displayName.startsWith('Heavy')) {
    console.warn(`Performance concern: ${updateInfo.displayName} re-rendered`);
    whyDidYouRender.defaultNotifier(updateInfo);
  }
};

whyDidYouRender(React, {
  notifier: filteredNotifier
});

Component Display Names

Utility for getting meaningful component names for logging:

/**
 * Gets a display name for a component
 * @param Component - React component to get name for
 * @returns String display name
 */
function getDisplayName(Component: React.ComponentType): string;

Component Type Detection

Utilities for identifying different types of React components:

/**
 * Determines if a component is a React.memo wrapped component
 * @param Component - Component to check
 * @returns Boolean indicating if it's a memo component
 */
function isMemoComponent(Component: any): boolean;

/**
 * Determines if a component is a React.forwardRef wrapped component
 * @param Component - Component to check
 * @returns Boolean indicating if it's a forwardRef component
 */
function isForwardRefComponent(Component: any): boolean;

/**
 * Determines if a component is a React class component
 * @param Component - Component to check
 * @returns Boolean indicating if it's a class component
 */
function isReactClassComponent(Component: any): boolean;

Tracking Eligibility

Function to determine if a component should be tracked based on configuration:

/**
 * Determines if a component should be tracked for re-renders
 * @param Component - Component to evaluate
 * @param options - Tracking context options
 * @returns Boolean indicating if component should be tracked
 */
function shouldTrack(
  Component: React.ComponentType, 
  options: { isHookChange?: boolean }
): boolean;

/**
 * Gets default props for a component (handles both function and class components)
 * @param Component - Component to get default props for
 * @returns Default props object or empty object
 */
function getDefaultProps(Component: React.ComponentType): object;

/**
 * Gets update information for a component re-render
 * @param options - Component and render information
 * @returns Structured update information for notifiers
 */
function getUpdateInfo(options: {
  Component: React.ComponentType;
  displayName: string;
  hookName?: string;
  prevHookResult?: any;
  nextHookResult?: any;
  prevProps?: any;
  nextProps?: any;
  prevState?: any;
  nextState?: any;
}): UpdateInfo;

Advanced Tracking Features

Owner Component Tracking

When logOwnerReasons is enabled, the library tracks which parent components cause child re-renders:

/**
 * Stores owner component data for render tracking
 * @param element - React element to store owner data for
 */
function storeOwnerData(element: React.Element): void;

/**
 * Gets the current owner component during rendering
 * @returns Current owner component or null
 */
function getCurrentOwner(): React.Component | null;

Hot Reload Integration

Built-in hot reload detection to prevent false positives during development:

/**
 * Creates a notifier that handles hot reload scenarios
 * @param hotReloadBufferMs - Buffer time in milliseconds
 * @returns Configured notifier function
 */
function createDefaultNotifier(hotReloadBufferMs?: number): Notifier;

Usage Example:

whyDidYouRender(React, {
  hotReloadBufferMs: 1000, // 1 second buffer
  trackAllPureComponents: true
});

Console Output Customization

Customize the appearance of console output:

interface ConsoleOptions {
  /** Color for component names (default: '#058') */
  titleColor?: string;
  
  /** Color for diff property names (default: 'blue') */
  diffNameColor?: string;
  
  /** Color for diff property paths (default: 'red') */
  diffPathColor?: string;
  
  /** Background color for text (default: 'white') */
  textBackgroundColor?: string;
  
  /** Use console.log instead of console.group (default: false) */
  onlyLogs?: boolean;
  
  /** Use console.groupCollapsed (default: false) */
  collapseGroups?: boolean;
}

Usage Example:

whyDidYouRender(React, {
  trackAllPureComponents: true,
  titleColor: '#ff6b6b',
  diffNameColor: '#4ecdc4',
  diffPathColor: '#45b7d1',
  collapseGroups: true
});

Install with Tessl CLI

npx tessl i tessl/npm-welldone-software--why-did-you-render@10.0.1

docs

component-tracking.md

core-setup.md

hook-tracking.md

index.md

tile.json