gotchajavascriptreactModerate
HOC pattern pitfalls — display names, prop forwarding, and hooks replacing them
Viewed 0 times
HOChigher order componentdisplayNamehoist staticsref forwardinghooks replacement
Problem
Higher-Order Components (HOCs) cause multiple issues: the wrapped component loses its displayName in DevTools (shows as 'Component'), static methods are not forwarded, refs are swallowed, and the component tree gains extra wrapper nodes for every HOC applied.
Solution
If you must write HOCs, follow these rules:
// 1. Set displayName
function withAuth(WrappedComponent) {
function WithAuth(props) {
const user = useAuth();
if (!user) return <Redirect to="/login" />;
return <WrappedComponent {...props} user={user} />;
}
WithAuth.displayName =
return WithAuth;
}
// 2. Forward refs
function withLogging(WrappedComponent) {
const WithLogging = forwardRef(function WithLogging(props, ref) {
console.log('render', props);
return <WrappedComponent {...props} ref={ref} />;
});
WithLogging.displayName =
return WithLogging;
}
// 3. Forward static methods
import hoistNonReactStatics from 'hoist-non-react-statics';
hoistNonReactStatics(WithAuth, WrappedComponent);
// Better: replace HOCs with custom hooks
function useAuth() {
const user = useContext(AuthContext);
if (!user) throw new Error('Not authenticated');
return user;
}
// 1. Set displayName
function withAuth(WrappedComponent) {
function WithAuth(props) {
const user = useAuth();
if (!user) return <Redirect to="/login" />;
return <WrappedComponent {...props} user={user} />;
}
WithAuth.displayName =
withAuth(${WrappedComponent.displayName || WrappedComponent.name});return WithAuth;
}
// 2. Forward refs
function withLogging(WrappedComponent) {
const WithLogging = forwardRef(function WithLogging(props, ref) {
console.log('render', props);
return <WrappedComponent {...props} ref={ref} />;
});
WithLogging.displayName =
withLogging(${WrappedComponent.name});return WithLogging;
}
// 3. Forward static methods
import hoistNonReactStatics from 'hoist-non-react-statics';
hoistNonReactStatics(WithAuth, WrappedComponent);
// Better: replace HOCs with custom hooks
function useAuth() {
const user = useContext(AuthContext);
if (!user) throw new Error('Not authenticated');
return user;
}
Why
HOCs wrap a component in another component, creating indirection in the component tree. Each layer adds a node to the DevTools tree and can swallow props, refs, and statics. Custom hooks achieve the same logic reuse without wrapping — they compose horizontally instead of vertically.
Gotchas
- Don't apply HOCs inside a render function — a new component type is created every render, causing full unmount/mount
- HOCs don't compose well — five HOCs applied to one component creates five nesting levels
- hoist-non-react-statics is a utility library to copy static methods to the wrapper
- TypeScript types for HOCs are complex — another reason to prefer hooks
Code Snippets
Never apply HOCs inside render
// BAD: HOC applied inside render
function Parent() {
const Enhanced = withStyles(Child); // new type every render!
return <Enhanced />;
}
// GOOD: apply outside component
const Enhanced = withStyles(Child);
function Parent() {
return <Enhanced />;
}Context
When working with or writing higher-order components in a React codebase
Revisions (0)
No revisions yet.