or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

index.mddocs/

Hoist Non React Statics

Hoist Non React Statics is a utility library for React higher-order components (HOCs) that safely copies non-React-specific static properties from a source component to a target component. It maintains React's static property blacklist to prevent overriding critical React internals while allowing the transfer of custom static methods and properties.

Package Information

  • Package Name: hoist-non-react-statics
  • Package Type: npm
  • Language: JavaScript
  • Installation:
    npm install hoist-non-react-statics

Core Imports

import hoistNonReactStatics from 'hoist-non-react-statics';

For CommonJS:

const hoistNonReactStatics = require('hoist-non-react-statics');

Basic Usage

import hoistNonReactStatics from 'hoist-non-react-statics';
import React from 'react';

// Source component with static properties
class MyComponent extends React.Component {
  static customMethod() {
    return 'custom';
  }
  
  static customProperty = 'value';
  
  render() {
    return <div>Hello World</div>;
  }
}

// Higher-order component
function withEnhancement(WrappedComponent) {
  class Enhancement extends React.Component {
    render() {
      return <WrappedComponent {...this.props} enhanced={true} />;
    }
  }
  
  // Hoist statics from the wrapped component
  return hoistNonReactStatics(Enhancement, WrappedComponent);
}

const EnhancedComponent = withEnhancement(MyComponent);

// EnhancedComponent now has the static properties from MyComponent
console.log(EnhancedComponent.customMethod()); // 'custom'
console.log(EnhancedComponent.customProperty); // 'value'

Capabilities

Static Property Hoisting

The main utility function that copies non-React-specific static properties from a source component to a target component.

/**
 * Copies non-react specific statics from a child component to a parent component
 * @param {object} targetComponent - The component to copy statics to
 * @param {object|string} sourceComponent - The component to copy statics from
 * @param {object} [blacklist] - Object with keys to exclude from hoisting
 * @returns {object} The targetComponent with hoisted static properties
 */
function hoistNonReactStatics(targetComponent, sourceComponent, blacklist);

Usage Examples:

Basic hoisting:

import hoistNonReactStatics from 'hoist-non-react-statics';

const Enhanced = hoistNonReactStatics(targetComponent, sourceComponent);

With blacklist to exclude specific statics:

hoistNonReactStatics(targetComponent, sourceComponent, {
  myStatic: true,
  myOtherStatic: true
});

Key Behaviors:

  • React Static Filtering: Automatically excludes React-specific statics (
    childContextTypes
    ,
    contextType
    ,
    contextTypes
    ,
    defaultProps
    ,
    displayName
    ,
    getDefaultProps
    ,
    getDerivedStateFromError
    ,
    getDerivedStateFromProps
    ,
    mixins
    ,
    propTypes
    ,
    type
    ) and JavaScript built-ins (
    name
    ,
    length
    ,
    prototype
    ,
    caller
    ,
    callee
    ,
    arguments
    ,
    arity
    )
  • ForwardRef Support: Properly handles React.forwardRef components (React 16.7+)
  • Memo Support: Correctly processes React.memo components
  • Prototype Chain Inheritance: Recursively traverses and hoists statics from the entire prototype chain, copying properties from parent classes up to
    Object.prototype
  • Symbol Properties: Supports both string and Symbol-based static properties
  • Property Descriptors: Preserves getters, setters, and other property attributes
  • Error Safety: Gracefully handles read-only properties that cannot be copied using try-catch blocks, silently skipping properties that throw errors during assignment
  • String Component Filtering: Skips hoisting when source is a string (HTML elements)

React Version Compatibility:

  • Version 3.x: React 0.13-16.x with ForwardRef support
  • Version 2.x: React 0.13-16.x without ForwardRef support
  • Version 1.x: React 0.13-16.2

Browser Compatibility:

  • Requires
    Object.defineProperty
    support
  • IE8 requires polyfill for
    Object.defineProperty

Parameters:

  • targetComponent
    (object): The React component to receive the copied static properties
  • sourceComponent
    (object|string): The React component to copy static properties from. If a string is provided, no hoisting occurs
  • blacklist
    (object, optional): An object where keys represent static property names to exclude from hoisting. Each key should have a truthy value

Return Value: Returns the

targetComponent
with the hoisted static properties applied.

Common Use Cases:

  1. Higher-Order Components: Preserve static methods when wrapping components
  2. Component Enhancement: Maintain API compatibility when adding functionality
  3. Component Composition: Combine multiple components while preserving their static interfaces
  4. Library Development: Create reusable component wrappers that don't lose static properties

Examples with Different Component Types:

Class components:

class Button extends React.Component {
  static variant = 'primary';
  static getDisplayText() { return 'Button'; }
  render() { return <button>{this.props.children}</button>; }
}

function withLogging(Component) {
  class LoggedComponent extends React.Component {
    componentDidMount() { console.log('mounted'); }
    render() { return <Component {...this.props} />; }
  }
  return hoistNonReactStatics(LoggedComponent, Component);
}

const LoggedButton = withLogging(Button);
// LoggedButton.variant === 'primary'
// LoggedButton.getDisplayText() === 'Button'

ForwardRef components:

const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="fancy" {...props} />
));
FancyButton.displayName = 'FancyButton';
FancyButton.customProp = 'custom';

function withWrapper(Component) {
  const Wrapped = React.forwardRef((props, ref) => (
    <div className="wrapper">
      <Component ref={ref} {...props} />
    </div>
  ));
  return hoistNonReactStatics(Wrapped, Component);
}

const WrappedFancyButton = withWrapper(FancyButton);
// WrappedFancyButton.customProp === 'custom'
// WrappedFancyButton.displayName remains as set on Wrapped component

Memo components:

const MemoButton = React.memo(props => <button {...props} />);
MemoButton.customStatic = 'value';

function enhance(Component) {
  function Enhanced(props) {
    return <Component {...props} enhanced={true} />;
  }
  return hoistNonReactStatics(Enhanced, Component);
}

const EnhancedMemoButton = enhance(MemoButton);
// EnhancedMemoButton.customStatic === 'value'

Inheritance chain example:

class BaseComponent extends React.Component {
  static baseMethod() { return 'base'; }
  static baseProp = 'base';
}

class ExtendedComponent extends BaseComponent {
  static extendedMethod() { return 'extended'; }
  static extendedProp = 'extended';
  // Instance methods are not hoisted
  instanceMethod() { return 'instance'; }
}

function withLogging(Component) {
  class LoggedComponent extends React.Component {
    componentDidMount() { console.log('Component mounted'); }
    render() { return <Component {...this.props} />; }
  }
  return hoistNonReactStatics(LoggedComponent, Component);
}

const LoggedExtended = withLogging(ExtendedComponent);
// LoggedExtended.baseMethod() === 'base' (from prototype chain)
// LoggedExtended.baseProp === 'base' (from prototype chain)
// LoggedExtended.extendedMethod() === 'extended' (direct property)
// LoggedExtended.extendedProp === 'extended' (direct property)
// LoggedExtended.instanceMethod === undefined (instance methods not hoisted)