Higher-order component for injecting internationalization functionality into class components, providing backward compatibility with legacy codebases.
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 />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" />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>
);
}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 />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)
);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>;
}/**
* 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;
}// 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>;
}// 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 };