
Higher-Order Components (HOC)
A higher-order component is a function that takes a component and returns a new component with added behavior. It’s a composition pattern that predates hooks.
Basic Pattern
function withLogging(WrappedComponent) {
function WithLogging(props) {
useEffect(() => {
console.log(`${WrappedComponent.displayName} mounted`);
return () => console.log(`${WrappedComponent.displayName} unmounted`);
}, []);
return <WrappedComponent {...props} />;
}
WithLogging.displayName = `WithLogging(${WrappedComponent.displayName ?? WrappedComponent.name})`;
return WithLogging;
}
const LoggedUserCard = withLogging(UserCard); Injecting Props
HOCs commonly inject props into the wrapped component:
function withCurrentUser(WrappedComponent) {
return function WithCurrentUser(props) {
const user = useCurrentUser();
return <WrappedComponent currentUser={user} {...props} />;
};
}
const ProfilePage = withCurrentUser(function({ currentUser }) {
return <div>Hello, {currentUser.name}</div>;
}); HOC Problems
Prop collision: injected props overwrite consumer props with the same name.
Wrapper hell: stacking HOCs creates deeply nested DevTools output:
withRouter(connect(mapState)(withStyles(styles)(withLogging(MyComponent)))) Static method loss: HOCs don’t forward static methods automatically. Use hoist-non-react-statics.
Ref forwarding: refs don’t pass through HOCs by default — wrap with forwardRef.
HOC to Hook Migration
Most HOC patterns translate directly to hooks:
// HOC
const Connected = withCurrentUser(MyComponent);
// Hook equivalent
function MyComponent() {
const user = useCurrentUser();
// ...
} Hooks are simpler, have no nesting overhead, and compose cleanly. For new code, prefer hooks.
When HOCs Still Make Sense
- Libraries that must support class components
- Wrapping third-party components you don’t control
- Codebases with established HOC conventions you’re extending









