A Higher-Order Component (HOC) is a function that takes a component and returns a new component with additional behavior or data.
pattern to abstract shared behavior
use composition and turn the HOC into a pure function - does not alter original arguments
// basic HOC structure
function withSomething(WrappedComponent) {
return function EnhancedComponent(props) {
return <WrappedComponent {...props} />;
};
}
// invalid structure
const HOC = (WrappedComponent) => {
// mutates the original component
WrappedComponent = () => {...};
}
// ex. Logging Properties
// add logging behavior to a component without changing the component
function withLogger(Component) {
return function LoggedComponent(props) {
console.log(props);
return <Component {...props} />;
};
}
// use
const UserWithLogger = withLogger(User);
// ex. Authentication
// conditionally render component based on authentication state
function withAuth(Component) {
return function Authenticated(props) {
if (!props.isLoggedIn) {
return <Login />;
}
return <Component {...props} />;
};
}
// use
const DashboardWithAuth = withAuth(Dashboard);
// ex. Data Injection
// provide shared data to a component without prop drilling
function withUser(Component) {
return function UserComponent(props) {
const user = { name: "Saima" };
return <Component {...props} user={user} />;
};
}
pass unrelated props through to the wrapped component
const withMousePosition = (WrappedComponent) => {
const injectedProp = {mousePosition: {x: 10, y: 10}};
return (originalProps) => {
return <WrappedComponent injectedProp={injectedProp} {...originalProps} />;
};
};
HOCs can accept additional arguments that act as extra configuration determining the type of enhancement the component receives
const EnhancedComponent = HOC(WrappedComponent, config)
Currying → functional programming pattern to maximize function composition. ex. used extensively in React libraries, such as React Redux
const EnhancedComponent = connect(selector, actions)(WrappedComponent);
// i.e.
const HOC = connect(selector, actions);
const EnhancedComponent = HOC(WrappedComponent);
compose(f, g, h) is the same as (...args) => f(g(h(...args)))
const enhance = compose(
// These are both single-argument HOCs
withMousePosition,
withURLLocation,
connect(selector)
);
// Enhance is a HOC
const EnhancedComponent = enhance(WrappedComponent);
Caveats
Don't use HOCs inside other components: always create your enhanced components outside any component scope. Otherwise, if you do so inside the body of other components and a re-render occurs, the enhanced component will be different. That forces React to remount it instead of just updating it, causing the component and its children to lose their previous state.
const Component = (props) => {
// This is wrong. Never do this
const EnhancedComponent = HOC(WrappedComponent);
return <EnhancedComponent />;
};
// This is the correct way
const EnhancedComponent = HOC(WrappedComponent);
const Component = (props) => {
return <EnhancedComponent />;
};
Refs aren’t passed through: since React refs are not props, they are handled specially by React. If you add a ref to an element whose component is the result of a HOC, the ref refers to an instance of the outermost container component, not the wrapped component. To solve this, you can use the React.forwardRef API

clientX and clientY are read-only JavaScript properties that give the horizontal (X) and vertical (Y) coordinates of a mouse or touch event, relative to the browser's visible viewport (the content area you see), not the entire web page or screenwindow object is the global object for all JavaScript code running in a web browser. It represents the browser's window or a frame and provides access to a wide range of browser functionalities and Web APIswindow.addEventListener() method in JavaScript is the standard, modern way to register an event handler for the entire browser window. It attaches a function that will be called whenever a specified event (e.g., 'load', 'resize', 'scroll', 'click') occurs on the window object.// use **with** to express enhancing nature of technique
const withSomething = (WrappedComponent) => {
return (props) => {
<WrappedComponent {...props} />
}
}
// define piece of local state to track mouse position
import {useState} from 'react';
// define global listener and window object for mouse move effect
import {useEffect} from 'react';
// HOC
const withMousePosition = (WrappedComponent) => {
return (props) => {
// track x + y coordinate of position using local **state**
const [mousePosition, setMousePosition] = useState({
x: 0,
y: 0
});
// create window event listener to get x + y position using **effect**
useEffect(() => {
// define callback function
const handleMousePositionChange = (e) => {
setMousePosition({
x: e.clientX;
y: e.clientY;
});
};
// **side effect** -> perform subscribe/unsubscribe logic
// create event listener
window.addEventListener("mousemove", handleMousePositionChange);
//cleanup
return () => {
window.removeEventListener("mousemove", handleMousePositionChange);
};
}, []);
// add new mousePosition prop
return <WrappedComponent {...props} mousePosition={mousePosition} />
};
};
// presentational components
// const logger = (prop) => {}
const PanelMouseLogger = ({mousePosition}) => {
if (!mousePosition) return null;
return (
<div className="BasicTracker">
<p>mouse position:</p>
<div className="Row">
<span>x: {mousePosition.x}</span>
<span>y: {mousePosition.y}</span>
</div>
</div>
);
};
const PointMouseLogger = ({mousePosition}) => {
if (!mousePosition) return null;
return (
<p>
({mousePosition.x}, {mousePosition.y})
</p>
);
};
// enhance both components using HOC
const PanelMouseTracker = withMousePosition(PanelMouseLogger);
const PointMouseTracker = withMousePosition(PointMouseLogger);
function App() {
return (
<div className="App">
<header className="Header">HOC Cursor Position Example</header>
<PanelMouseTracker />
<PointMouseTracker />
</div>
);
}