A distillation of *“Learning Patterns” by Lydia Hallie & Addy Osmani***
Definition (book’s wording): A Higher‑Order Component is a function that takes a component and returns a new component with additional props, state, or behaviour. It’s the React equivalent of higher‑order functions in JavaScript.
1 Why the Authors Still Care About HOCs
Even after Hooks became mainstream, Hallie & Osmani argue that HOCs remain an essential pattern for three reasons:
Reason | What it means | In practice |
---|---|---|
Separation of concerns | Keep UI logic (rendering) isolated from cross‑cutting logic (data‑fetching, auth, analytics). | A withAuth HOC wraps any page that needs user context without bloating the component tree. |
Composable re‑use | You can chain many tiny enhancers without inheritance hell. | withRouter(withTheme(withTracker(Component))) |
Library interoperability | Vast ecosystem built pre‑Hooks still relies on HOCs, so understanding them is key to maintaining legacy codebases. |
connect from Redux or withStyles from Material‑UI. |
“HOCs shine when you need to add behaviour to multiple components without repeating yourself.” — Learning Patterns
2 Anatomy of an HOC
// A tiny logger HOC
function withLogger(WrappedComponent) {
return function LoggedComponent(props) {
useEffect(() => {
console.log(`[log] ${WrappedComponent.name} mounted`);
return () => console.log(`[log] ${WrappedComponent.name} unmounted`);
}, []);
return <WrappedComponent {...props} />; // forward all props
};
}
-
Input →
WrappedComponent
. - Enhancement → side‑effects, new props, conditional rendering.
-
Output →
LoggedComponent
, ready to drop anywhere.
Tip from the book: always copy static properties (displayName
, defaultProps
) with hoist-non-react-statics
to avoid surprises.
3 5 Real‑World Use‑Cases You’ve Probably Used
# | Library / Pattern | What the HOC adds | Where you’ll see it |
---|---|---|---|
1 | connect (react‑redux) |
Subscribes to the Redux store and injects state + dispatch props. |
Any React+Redux app prior to useSelector . |
2 | withRouter (react‑router v5) |
Gives access to history , location , match without prop‑drilling. |
Routing logic in class components. |
3 | withApollo (Apollo Client) |
Binds GraphQL client and query cache helpers. | Data‑heavy dashboards built before the useQuery hook. |
4 | withTranslation (react‑i18next) |
Injects t translation function and current locale. |
Multi‑language SaaS dashboards. |
5 | withStyles / makeStyles (Material‑UI) |
Provides dynamically generated CSS‑in‑JS classes as props. | Design‑system heavy UIs needing theme overrides. |
Each example underscores the authors’ point: HOCs wrap cross‑cutting behaviour, freeing your components to focus on rendering.
4 Chaining & Composition
Because an HOC just returns another component, you can compose them like functions:
// Compose multiple enhancers
export default compose(
withRouter,
connect(mapState, mapDispatch),
withLogger
)(Dashboard);
The book warns to apply them inside‑out (i.e., right‑to‑left) so each HOC receives the component returned by the next.
5 Caveats & Best Practices from the Book
Pitfall | Mitigation |
---|---|
Prop collisions (two HOCs supply the same prop) | Prefix / namespace injected props (analyticsData , i18nProps ). |
Debugging depth – many wrappers make React DevTools noisy | Give each HOC a clear displayName (withLogger(HomePage) ). |
Static method loss – wrapped component’s statics disappear | Use hoist-non-react-statics . |
Performance – extra renders if HOC doesn’t memoise | Pass React.memo ‑ised components or leverage shouldComponentUpdate . |
Hallie & Osmani’s bottom line: prefer Hooks for new code, but reach for HOCs when you need legacy compatibility, type‑erasure (class vs function agnostic), or behaviour that must be attached outside component logic (e.g., error boundaries in class era).
6 When to Choose an HOC Over Other Patterns
- You need to wrap class components (where hooks aren’t possible).
- The behaviour touches multiple lifecycle stages (mount, update, unmount) across many components.
- You want an opt‑in enhancement—components can take it or leave it.
“Hooks, Render Props, and HOCs all coexist. Choose the one that expresses intent most clearly for your team.” — Learning Patterns
7 Sample Migration Path (HOC ➜ Hook)
/* OLD */
export default connect(mapState)(TodoList);
/* NEW */
function TodoList() {
const todos = useSelector(selectTodos);
return todos.map(t => <TodoItem key={t.id} {...t} />);
}
Hooks reduce wrapper depth but the underlying concept—encapsulating reusable behaviour—remains identical. The pattern evolves; the principle sticks.
Conclusion
Higher‑Order Components might feel “old school,” but they still solve real problems. Master them, and the ecosystem of mature libraries becomes far less intimidating.
Content adapted from “Learning Patterns” (Patterns.dev, CC BY‑NC 4.0).
Top comments (0)