DEV Community

Cover image for Higher‑Order Components (HOC) — Power‑Ups for React Components
Md Enayetur Rahman
Md Enayetur Rahman

Posted on

Higher‑Order Components (HOC) — Power‑Ups for React Components

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
  };
}
Enter fullscreen mode Exit fullscreen mode
  1. InputWrappedComponent.
  2. Enhancement → side‑effects, new props, conditional rendering.
  3. OutputLoggedComponent, 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);
Enter fullscreen mode Exit fullscreen mode

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} />);
}
Enter fullscreen mode Exit fullscreen mode

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)