All three share logic between components, but they evolved over time and custom hooks are the modern default for sharing logic.
Higher-Order Component (HOC)
A function that takes a component and returns an enhanced one:
jsx
const withAuth = Component => props => {
const user = useUser();
return user ? <Component {...props} user={user} /> : <Login />;
};
const ProtectedPage = withAuth(Page);
Downsides: wrapper nesting ("wrapper hell" in the tree), prop name collisions, and it's unclear where injected props come from.
Render props
A component takes a function as its child to share state:
jsx
<Mouse>{({ x, y }) => <Cursor x={x} y={y} />}</Mouse>
Flexible, but nesting several render-prop components creates a "callback pyramid."
Custom hooks (preferred)
Extract the stateful logic into a useX function — no extra tree nesting, explicit data flow:
jsx
function useMouse() {
const [pos, setPos] = useState({ x: 0, y: 0 });
useEffect(() => { /* attach listener, cleanup */ }, []);
return pos;
}
function Component() {
const { x, y } = useMouse(); // clear, flat, composable
}
How to choose
- Sharing logic → custom hooks (the modern answer).
- Sharing/structuring UI → plain composition (passing
children/components). - HOCs / render props are still useful when integrating older libraries or when you must wrap the rendering itself (e.g. some animation libraries). They're not wrong — just superseded by hooks for most logic reuse.
