Memoization is never free. Each useMemo or useCallback allocates a dependency array on every render and compares it entry by entry; each memo retains the last props and output while the component is mounted. Against a calculation that takes a microsecond, the bookkeeping costs more than the work. Four patterns lose outright:
A memoized component with an unstable prop. The comparison runs, fails, and the component renders anyway. The Unstable child in memo pays for memo four times and benefits zero times.
Memoizing what is already stable. State setters, dispatch, refs and imported functions never change identity. Wrapping them changes nothing and adds a line a reader must verify.
Dependency arrays tuned by hand. Removing an entry to "make it stable" produces a stale closure, invisible until a user reports wrong data; adding one that changes every render turns the Hook into pure overhead.
A component defined inside another component. This one destroys memoization completely, and it is common.
function Page({ rows }) {
const Row = memo(({ row }) => <li>{row.title}</li>); // a NEW component type each render
return <ul>{rows.map((row) => <Row key={row.id} row={row} />)}</ul>;
}memo returns a new component object every time Page runs, so React 7,897 sees a different element type, throws away the whole subtree and remounts it — losing its DOM nodes, its state and its Effects along with the cache. Define components at module scope; the only correct place for memo is next to that definition.