I've rewritten the same React component patterns about a dozen times across different projects. Each rewrite teaches me something new about what 'maintainable' actually means in practice. Here are six patterns I now start with, not arrive at after a painful refactor.
1. Compound Components
// Monolithic (hard to customise)
<Modal title="Confirm" body="Are you sure?" onConfirm={fn} onCancel={fn} />
// Compound (flexible, readable)
<Modal>
<Modal.Header>Confirm</Modal.Header>
<Modal.Body>Are you sure?</Modal.Body>
<Modal.Footer>
<Button onClick={onCancel}>Cancel</Button>
<Button variant="primary" onClick={onConfirm}>Yes</Button>
</Modal.Footer>
</Modal>2. Custom Hooks for Behaviour, Components for UI
function useInfiniteScroll(loadMore) {
const ref = useRef(null);
useEffect(() => {
const obs = new IntersectionObserver(entries => {
if (entries[0].isIntersecting) loadMore();
});
if (ref.current) obs.observe(ref.current);
return () => obs.disconnect();
}, [loadMore]);
return ref;
}3. The Controlled Input Pattern
Uncontrolled inputs (using ref to read values) are fine for simple forms. But for any form with validation, interdependent fields, or submit logic, controlled inputs where React owns the state are far easier to reason about.
4. Error Boundaries for Resilient UIs
React error boundaries catch JavaScript errors in their child component tree and display a fallback UI. Without them, one unhandled error can blank out your entire page. I wrap every major page section in an error boundary.
5. Render Props for Cross-Cutting Concerns
When two completely different components need the same behaviour (e.g. tracking mouse position, measuring element size), a render prop or hook extracts it without forcing a shared parent.
6. Memoisation — Sparingly
Premature optimisation is the root of all evil. This applies to React hooks too.
Profile first. Memoise only when you measure a real performance problem, not as a precaution.
These patterns aren't rules. They're starting points. The best code is the simplest code that solves the problem.
