CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-hot-loader

Tweak React components in real time during development with Hot Module Replacement while preserving component state.

Pending
Overview
Eval results
Files

component-utilities.mddocs/

Component Utilities

React Hot Loader provides utility functions for component comparison, cold component marking, and hot reload control. These utilities help manage component hot reloading behavior and provide fine-grained control over the hot reload process.

Capabilities

Component Comparison

Tests whether two React component types are equal, taking into account proxy components used by hot reloading.

/**
 * Tests if types of two components are equal
 * @param typeA - First component type
 * @param typeB - Second component type  
 * @returns true if components are equal (considering hot reload proxies)
 */
function areComponentsEqual<T>(typeA: React.ComponentType<T>, typeB: React.ComponentType<T>): boolean;

Usage Examples:

import { areComponentsEqual } from 'react-hot-loader';

const ComponentA = () => <div>A</div>;
const ComponentB = () => <div>B</div>;

// Basic comparison
console.log(areComponentsEqual(ComponentA, ComponentA)); // true
console.log(areComponentsEqual(ComponentA, ComponentB)); // false

// Hot reload scenario
const OriginalComponent = () => <div>Original</div>;
// After hot reload, component reference changes but areComponentsEqual 
// will return true if it's the same logical component
const hotReloadedComponent = () => <div>Updated</div>;

// This accounts for hot reload proxies
console.log(areComponentsEqual(OriginalComponent, hotReloadedComponent)); 
// May return true if they're the same component after hot reload
// TypeScript usage with generic types
interface Props {
  name: string;
}

const UserComponent: React.FC<Props> = ({ name }) => <div>{name}</div>;
const AdminComponent: React.FC<Props> = ({ name }) => <div>Admin: {name}</div>;

const isSameComponent = areComponentsEqual(UserComponent, AdminComponent);
console.log(isSameComponent); // false
// Testing in component lifecycle
class MyComponent extends React.Component {
  shouldComponentUpdate(nextProps) {
    // Check if the component type has changed due to hot reload
    return !areComponentsEqual(this.constructor, nextProps.componentType);
  }
  
  render() {
    return <div>My Component</div>;
  }
}

Cold Component Marking

Marks a component as "cold", making it invisible to React Hot Loader. Changes to cold components will cause local state loss and full re-mounting.

/**
 * Marks component as cold, making it invisible for React-Hot-Loader
 * Any changes to that component will cause local state loss
 * @param component - Component to mark as cold
 * @returns The same component (unchanged)
 */
function cold<T = React.ComponentType<any>>(component: T): T;

Usage Examples:

import React from 'react';
import { cold } from 'react-hot-loader';

// Mark a component as cold
const StaticComponent = cold(() => {
  return <div>This component won't hot reload</div>;
});

export default StaticComponent;
// Changes to this component will cause full re-mount, losing state
// Cold class component
import { cold } from 'react-hot-loader';

class DatabaseConnection extends React.Component {
  constructor(props) {
    super(props);
    this.connection = establishConnection();
  }
  
  componentWillUnmount() {
    this.connection.close();
  }
  
  render() {
    return <div>Connected to database</div>;
  }
}

// Mark as cold to prevent hot reload interference with database connection
export default cold(DatabaseConnection);
// TypeScript cold component
import { cold } from 'react-hot-loader';

interface CriticalProps {
  securityToken: string;
}

const SecurityComponent: React.FC<CriticalProps> = ({ securityToken }) => {
  // This component handles sensitive operations
  return <div>Secure Area</div>;
};

// Mark as cold for security reasons - always full reload
export default cold(SecurityComponent);
// Conditional cold marking
import { cold } from 'react-hot-loader';

const MyComponent = () => <div>My Component</div>;

// Only mark as cold in certain conditions
const shouldBeCold = process.env.DISABLE_HOT_RELOAD === 'true';
const ExportedComponent = shouldBeCold ? cold(MyComponent) : MyComponent;

export default ExportedComponent;

Advanced Usage Patterns

Selective Hot Reloading

import { hot, cold } from 'react-hot-loader';

// Hot reload for UI components
const UIComponent = hot(module)(() => <div>UI Component</div>);

// Cold for critical system components  
const SystemComponent = cold(() => <div>System Component</div>);

// Mixed usage in app
const App = () => (
  <div>
    <UIComponent /> {/* Will hot reload and preserve state */}
    <SystemComponent /> {/* Will full reload and reset state */}
  </div>
);

export default hot(module)(App);

Component Comparison in Testing

import { areComponentsEqual } from 'react-hot-loader';
import { render } from '@testing-library/react';

describe('Component equality', () => {
  it('should recognize same components', () => {
    const ComponentA = () => <div>Test</div>;
    const ComponentB = () => <div>Test</div>;
    
    // In testing, these are different functions
    expect(areComponentsEqual(ComponentA, ComponentA)).toBe(true);
    expect(areComponentsEqual(ComponentA, ComponentB)).toBe(false);
  });
  
  it('should handle hot reloaded components', () => {
    // Mock hot reload scenario
    const originalComponent = () => <div>Original</div>;
    
    // Simulate hot reload by creating new component with same "identity"
    const reloadedComponent = () => <div>Reloaded</div>;
    
    // In actual hot reload, these would be considered equal
    // Test your specific hot reload logic here
  });
});

Error Boundary with Cold Components

import React from 'react';
import { cold } from 'react-hot-loader';

// Error boundary should be cold to prevent hot reload issues
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }
  
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  
  componentDidCatch(error, errorInfo) {
    console.error('Error caught by boundary:', error, errorInfo);
  }
  
  render() {
    if (this.state.hasError) {
      return <div>Something went wrong.</div>;
    }
    
    return this.props.children;
  }
}

// Mark error boundary as cold for reliability
export default cold(ErrorBoundary);

Third-Party Component Integration

import { cold } from 'react-hot-loader';
import SomeThirdPartyComponent from 'some-library';

// Third-party components often don't work well with hot reload
const SafeThirdPartyComponent = cold(SomeThirdPartyComponent);

// Wrapper component can still be hot
const MyWrapper = ({ data }) => (
  <div>
    <h1>My App</h1>
    <SafeThirdPartyComponent data={data} />
  </div>
);

export default hot(module)(MyWrapper);

Performance Considerations

When to Use Cold Components

// ✅ Good candidates for cold():
// - Components with complex initialization
// - Components that manage external resources
// - Third-party components
// - Error boundaries
// - Components with side effects in constructor

// ❌ Avoid cold() for:
// - Simple presentational components
// - Components you frequently modify
// - Components where state preservation is important

Component Comparison Performance

// areComponentsEqual() is lightweight but consider caching for heavy usage
const componentComparison = useMemo(() => {
  return areComponentsEqual(ComponentA, ComponentB);
}, [ComponentA, ComponentB]);

Debugging Component Issues

import { areComponentsEqual, cold } from 'react-hot-loader';

const DebugComponent = ({ children }) => {
  useEffect(() => {
    // Debug component equality issues
    console.log('Component equality check:', 
      areComponentsEqual(children.type, children.type)
    );
  }, [children]);
  
  return children;
};

// Use cold for debugging to avoid hot reload interference
export default cold(DebugComponent);

Migration Notes

Legacy Component Comparison

// Old way (manual proxy checking)
const getActualComponent = (component) => {
  return component.__hotReloadProxy ? 
    component.__hotReloadProxy.getCurrent() : 
    component;
};

const manualComparison = (a, b) => {
  return getActualComponent(a) === getActualComponent(b);
};

// New way (recommended)
import { areComponentsEqual } from 'react-hot-loader';
const modernComparison = areComponentsEqual(a, b);

Component Configuration

Sets specific options for individual components, providing fine-grained control over hot reload behavior for specific components.

/**
 * Configure options for a specific component
 * @param component - Component to configure
 * @param options - Configuration options for the component
 * @returns Configured component
 */
function configureComponent(component: React.ComponentType<any>, options: any): React.ComponentType<any>;

Usage Examples:

import { configureComponent } from 'react-hot-loader';

const MyComponent = () => <div>My Component</div>;

// Configure component with specific options
const ConfiguredComponent = configureComponent(MyComponent, {
  pureSFC: true,
  pureRender: false
});

export default ConfiguredComponent;
// Configure multiple components differently
const FastComponent = configureComponent(ComponentA, { 
  pureSFC: true,
  pureRender: true 
});

const RichComponent = configureComponent(ComponentB, { 
  pureSFC: false,
  allowSFC: true 
});

Cold Component Best Practices

// ✅ Good: Export cold components directly
export default cold(MyComponent);

// ✅ Good: Conditional cold based on environment
export default process.env.NODE_ENV === 'test' 
  ? cold(MyComponent) 
  : hot(module)(MyComponent);

// ❌ Avoid: Mixing hot and cold on same component
const Component = hot(module)(cold(MyComponent)); // Don't do this

// ❌ Avoid: Cold inside render methods
const BadExample = () => {
  const ColdComponent = cold(SomeComponent); // Don't do this
  return <ColdComponent />;
};

Install with Tessl CLI

npx tessl i tessl/npm-react-hot-loader

docs

app-container.md

build-integration.md

component-utilities.md

configuration.md

hot-wrapper.md

index.md

tile.json