Higher-Order Component
Problem
You copy-paste the same logic across multiple components - authentication checks, data fetching, analytics tracking, or error handling. Every component duplicates this behavior, making updates tedious and error-prone as you hunt down every instance to keep them in sync.
Solution
Wrap components in functions that add behavior or props, creating reusable enhancement patterns. This was popular before hooks but can still be useful for library APIs and cross-cutting concerns.
Example
This example demonstrates a higher-order component that adds authentication checking to any component, showing a login form if the user is not authenticated.
// HOC function that wraps a component class with authentication logic
function withAuth(ComponentClass) {
return class AuthenticatedComponent extends ComponentClass {
async connectedCallback() {
// Check if user is authenticated
const user = await authService.getUser();
// If not authenticated, show login form instead of component
if (!user) {
this.innerHTML = '<login-form></login-form>';
return;
}
// If authenticated, inject user and render original component
this.user = user;
super.connectedCallback?.();
}
};
}
// Original component expects user to be available
class Dashboard extends HTMLElement {
connectedCallback() {
this.innerHTML = `<h1>Welcome ${this.user.name}</h1>`;
}
}
// Wrap Dashboard with authentication - creates an enhanced version
const ProtectedDashboard = withAuth(Dashboard);
customElements.define('protected-dashboard', ProtectedDashboard);
Benefits
- Enables code reuse by extracting common behavior into reusable wrappers.
- Keeps components focused on presentation while HOCs handle cross-cutting concerns.
- Works well for library APIs that need to enhance multiple components.
- Provides composition patterns for adding authentication, logging, or error boundaries.
Tradeoffs
- Creates wrapper components that add layers to the React component tree.
- Can lead to prop naming collisions when multiple HOCs wrap the same component.
- Makes debugging harder as stack traces show wrapper components instead of originals.
- Largely superseded by hooks which provide cleaner composition for most use cases.