function shallowEqual(a, b) {
return Object.keys(a).every(k => a[k] === b[k]);
}
let childRenders = 0;
function Child({ onClick, label }) {
childRenders++;
return label;
}
function MemoChild(props) {
if (MemoChild._prev && shallowEqual(MemoChild._prev, props)) return 'skip';
MemoChild._prev = props;
return Child(props);
}
// Parent re-renders 3 times, creating a new function each time
for (let i = 0; i < 3; i++) {
const handleClick = () => console.log('clicked'); // new function each render!
MemoChild({ onClick: handleClick, label: 'btn' });
}
console.log(childRenders); // should be 1, is 3function shallowEqual(a, b) {
return Object.keys(a).every(k => a[k] === b[k]);
}
let childRenders = 0;
function Child({ onClick, label }) {
childRenders++;
return label;
}
function MemoChild(props) {
if (MemoChild._prev && shallowEqual(MemoChild._prev, props)) return 'skip';
MemoChild._prev = props;
return Child(props);
}
// Fix: stable function reference (created once, reused)
const stableHandleClick = () => console.log('clicked'); // useCallback([])
for (let i = 0; i < 3; i++) {
MemoChild({ onClick: stableHandleClick, label: 'btn' });
}
console.log(childRenders);Bug: handleClick is recreated on every render — new function reference each time. shallowEqual sees onClick changed (old fn !== new fn) and re-renders.
Explanation: stableHandleClick is the same reference each render. shallowEqual sees onClick === onClick — child is skipped on renders 2 and 3.
Key Insight: React.memo is only effective when ALL props are stable. Inline arrow functions break it. Wrap callbacks with useCallback to get stable references: const handleClick = useCallback(() => {}, []).