Chapter 3 of 4
Render Props and Higher Order Components
The two pre-hooks reuse patterns, why they faded, and where they still fit.
Before hooks, sharing stateful logic meant sharing components. Two patterns dominated, and you will still meet both in existing codebases and libraries.
Render props
A component holds the logic and calls a function prop to decide what to render.
function MousePosition({ render }) {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
function handleMove(event) {
setPosition({ x: event.clientX, y: event.clientY });
}
window.addEventListener("mousemove", handleMove);
return () => window.removeEventListener("mousemove", handleMove);
}, []);
return render(position);
}
<MousePosition render={({ x, y }) => <p>{x}, {y}</p>} />Higher order components
A HOC is a function that takes a component and returns a new one with extra props or behaviour.
function withUser(Component) {
return function WithUser(props) {
const user = useContext(UserContext);
return <Component {...props} user={user} />;
};
}
const ProfileWithUser = withUser(Profile);Why hooks replaced both
- Wrapper hell - several HOCs nest components deep enough to make DevTools unreadable.
- Prop collisions - two HOCs that both inject
datasilently overwrite each other. - Indirection - it is not obvious from a component where an injected prop came from.
- A custom hook has none of these problems: no extra components, no injected props, and the source is visible at the call site.
// A HOC and its hook equivalent
const Enhanced = withUser(withTheme(withRouter(Profile)));
function Profile() {
const user = useUser();
const theme = useTheme();
const router = useRouter();
}Where HOCs still make sense
- Wrapping a component in an error boundary or a provider, where you genuinely want an extra component.
React.memoandforwardRefare themselves HOCs.- Library integrations that must work with class components.