or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

date-time.mdhooks.mdindex.mdinject-intl.mdmessages.mdnumber-list.mdprovider.md
tile.json

inject-intl.mddocs/

Legacy HOC Support

Higher-order component for injecting internationalization functionality into class components, providing backward compatibility with legacy codebases.

Capabilities

injectIntl HOC

Higher-order component that injects an intl prop containing the IntlShape object into wrapped components.

/**
 * Higher-order component for injecting intl prop into class components
 * Provides backward compatibility for legacy React class components
 * @param WrappedComponent - Component to wrap with intl functionality
 * @param options - Configuration options for injection behavior
 * @returns Enhanced component with intl prop injected
 */
function injectIntl<
  IntlPropName extends string = 'intl',
  P extends WrappedComponentProps<IntlPropName> = WrappedComponentProps<any>
>(
  WrappedComponent: React.ComponentType<P>,
  options?: InjectIntlOptions<IntlPropName>
): React.ComponentType<WithIntlProps<P>>;

interface InjectIntlOptions<IntlPropName extends string = 'intl'> {
  /** Name of the prop to inject (defaults to 'intl') */
  intlPropName?: IntlPropName;
  /** Whether to forward refs to the wrapped component */
  forwardRef?: boolean;
  /** Whether to enforce IntlProvider context existence */
  enforceContext?: boolean;
}

Usage Examples:

import React from 'react';
import { injectIntl, WrappedComponentProps } from 'react-intl';

// Class component with intl prop
class LegacyComponent extends React.Component<WrappedComponentProps> {
  render() {
    const { intl } = this.props;
    
    const greeting = intl.formatMessage({
      id: 'greeting',
      defaultMessage: 'Hello!'
    });
    
    const formattedDate = intl.formatDate(new Date());
    
    return (
      <div>
        <h1>{greeting}</h1>
        <p>Today is {formattedDate}</p>
      </div>
    );
  }
}

// Inject intl prop
const EnhancedLegacyComponent = injectIntl(LegacyComponent);

// Usage
<EnhancedLegacyComponent />

Custom Prop Name

Customize the name of the injected prop for flexibility or conflict avoidance.

/**
 * Inject intl with custom prop name
 */
function injectIntl<IntlPropName extends string>(
  WrappedComponent: React.ComponentType<{ [K in IntlPropName]: IntlShape } & any>,
  options: { intlPropName: IntlPropName }
): React.ComponentType<any>;

Usage Examples:

import React from 'react';
import { injectIntl } from 'react-intl';

// Component expecting custom prop name
interface Props {
  i18n: IntlShape;
  name: string;
}

class CustomPropComponent extends React.Component<Props> {
  render() {
    const { i18n, name } = this.props;
    
    const welcome = i18n.formatMessage(
      { id: 'welcome', defaultMessage: 'Welcome {name}!' },
      { name }
    );
    
    return <div>{welcome}</div>;
  }
}

// Inject with custom prop name
const EnhancedCustomComponent = injectIntl(CustomPropComponent, {
  intlPropName: 'i18n'
});

// Usage
<EnhancedCustomComponent name="Alice" />

Ref Forwarding

Enable ref forwarding for accessing wrapped component instances.

/**
 * Inject intl with ref forwarding support
 */
function injectIntl<P>(
  WrappedComponent: React.ComponentType<P>,
  options: { forwardRef: true }
): React.ForwardRefExoticComponent<WithIntlProps<P> & React.RefAttributes<any>>;

Usage Examples:

import React from 'react';
import { injectIntl, WrappedComponentProps } from 'react-intl';

class RefForwardingComponent extends React.Component<WrappedComponentProps> {
  private inputRef = React.createRef<HTMLInputElement>();
  
  focus = () => {
    this.inputRef.current?.focus();
  };
  
  render() {
    const { intl } = this.props;
    const placeholder = intl.formatMessage({
      id: 'search.placeholder',
      defaultMessage: 'Search...'
    });
    
    return (
      <input 
        ref={this.inputRef}
        placeholder={placeholder}
        type="text"
      />
    );
  }
}

// Enable ref forwarding
const EnhancedRefComponent = injectIntl(RefForwardingComponent, {
  forwardRef: true
});

// Usage with ref
function ParentComponent() {
  const componentRef = React.useRef<RefForwardingComponent>(null);
  
  const handleFocus = () => {
    componentRef.current?.focus();
  };
  
  return (
    <div>
      <EnhancedRefComponent ref={componentRef} />
      <button onClick={handleFocus}>Focus Input</button>
    </div>
  );
}

Context Enforcement

Control whether to enforce IntlProvider context existence.

/**
 * Inject intl with optional context enforcement
 */
function injectIntl<P>(
  WrappedComponent: React.ComponentType<P>,
  options?: { enforceContext?: boolean }
): React.ComponentType<WithIntlProps<P>>;

Usage Examples:

import React from 'react';
import { injectIntl, WrappedComponentProps } from 'react-intl';

class OptionalIntlComponent extends React.Component<WrappedComponentProps> {
  render() {
    const { intl } = this.props;
    
    // intl might be null if no IntlProvider exists
    if (!intl) {
      return <div>No internationalization available</div>;
    }
    
    const message = intl.formatMessage({
      id: 'optional.message',
      defaultMessage: 'Optional message'
    });
    
    return <div>{message}</div>;
  }
}

// Disable context enforcement
const FlexibleComponent = injectIntl(OptionalIntlComponent, {
  enforceContext: false
});

// Can be used outside IntlProvider without throwing
<FlexibleComponent />

Complex HOC Patterns

Advanced patterns for complex component hierarchies and prop manipulation.

Usage Examples:

import React from 'react';
import { injectIntl, WrappedComponentProps } from 'react-intl';

// Component with additional props
interface ComponentProps extends WrappedComponentProps {
  title: string;
  count: number;
  onAction: () => void;
}

class ComplexComponent extends React.Component<ComponentProps> {
  render() {
    const { intl, title, count, onAction } = this.props;
    
    const formattedTitle = intl.formatMessage({
      id: 'component.title',
      defaultMessage: '{title} ({count})'
    }, { title, count });
    
    const actionLabel = intl.formatMessage({
      id: 'action.label',
      defaultMessage: 'Take Action'
    });
    
    return (
      <div>
        <h2>{formattedTitle}</h2>
        <button onClick={onAction}>{actionLabel}</button>
      </div>
    );
  }
}

// Enhanced component maintains original prop interface
const EnhancedComplexComponent = injectIntl(ComplexComponent);

// Usage maintains type safety
<EnhancedComplexComponent 
  title="Dashboard"
  count={5}
  onAction={() => console.log('Action!')}
/>

// HOC composition
const withLogging = <P extends object>(Component: React.ComponentType<P>) => {
  return class extends React.Component<P> {
    componentDidMount() {
      console.log('Component mounted');
    }
    
    render() {
      return <Component {...this.props} />;
    }
  };
};

// Compose HOCs
const EnhancedWithLogging = withLogging(
  injectIntl(ComplexComponent)
);

Migration from Class to Hooks

Pattern for gradually migrating from HOC to hooks.

Usage Examples:

import React from 'react';
import { injectIntl, useIntl, WrappedComponentProps } from 'react-intl';

// Original class component
class LegacyClassComponent extends React.Component<WrappedComponentProps> {
  render() {
    const { intl } = this.props;
    return <div>{intl.formatMessage({ id: 'message' })}</div>;
  }
}

// Migration wrapper using hooks
function ModernWrapper() {
  const intl = useIntl();
  
  // Pass intl as prop to legacy component
  return <LegacyClassComponent intl={intl} />;
}

// Gradually replace class components
function FullyModernComponent() {
  const intl = useIntl();
  const message = intl.formatMessage({ id: 'message' });
  return <div>{message}</div>;
}

Types

/**
 * Props interface for components that receive intl prop
 */
type WrappedComponentProps<IntlPropName extends string = 'intl'> = {
  [K in IntlPropName]: IntlShape;
};

/**
 * Utility type for components using injectIntl HOC
 * Removes intl prop from component props interface
 */
type WithIntlProps<P> = Omit<P, keyof WrappedComponentProps> & {
  forwardedRef?: React.Ref<any>;
};

/**
 * Distributed omit utility for union types
 */
type DistributedOmit<T, K extends PropertyKey> = T extends unknown
  ? Omit<T, K>
  : never;

/**
 * Options for configuring injectIntl behavior
 */
interface InjectIntlOptions<IntlPropName extends string = 'intl'> {
  intlPropName?: IntlPropName;
  forwardRef?: boolean;
  enforceContext?: boolean;
}

Migration Guidelines

From injectIntl to useIntl

// Before: HOC pattern
class OldComponent extends React.Component<WrappedComponentProps> {
  render() {
    const { intl } = this.props;
    return <div>{intl.formatMessage({ id: 'hello' })}</div>;
  }
}
const EnhancedOldComponent = injectIntl(OldComponent);

// After: Hooks pattern
function NewComponent() {
  const intl = useIntl();
  return <div>{intl.formatMessage({ id: 'hello' })}</div>;
}

Maintaining Backward Compatibility

// Hybrid approach for gradual migration
interface Props {
  name: string;
}

// Modern implementation
function ModernImplementation({ name }: Props) {
  const intl = useIntl();
  const greeting = intl.formatMessage(
    { id: 'greeting', defaultMessage: 'Hello {name}!' },
    { name }
  );
  return <div>{greeting}</div>;
}

// Legacy wrapper for existing usage
const LegacyWrapper = injectIntl(
  class extends React.Component<Props & WrappedComponentProps> {
    render() {
      const { intl, ...props } = this.props;
      return (
        <IntlProvider locale={intl.locale} messages={intl.messages}>
          <ModernImplementation {...props} />
        </IntlProvider>
      );
    }
  }
);

// Export both for flexibility
export { ModernImplementation as MyComponent, LegacyWrapper as MyComponentLegacy };