- React
- Performance Engineering, Profiling, and Virtualization
- React.memo & Custom Comparator Functions
React.memo & Custom Comparator Functions
React.memo is a higher-order component that skips re-rendering when props are shallowly equal (Object.is). You can pass a custom arePropsEqual(prevProps, nextProps) comparator, returning true to skip re-rendering or false to re-render.
Key Learning Objectives
Understand how React.memo performs shallow prop comparison using Object.is.
Implement a custom arePropsEqual comparator function to handle nested objects.
Recognize why passing inline callbacks or object literals bypasses React.memo.
Contrast arePropsEqual (return true to skip) with shouldComponentUpdate (return false to skip).
The Interview Problem & Context
What is logged to the console when comparing a standard React.memo component with inline props against a React.memo component with a custom arePropsEqual comparator?
1// Simulating React.memo comparison behavior2function simulateReactMemo() {3 const logs: string[] = [];45 // 1. Standard shallow comparison with new inline object reference:6 const obj1 = { id: 1 };7 const obj2 = { id: 1 };8 const isShallowEqual = Object.is(obj1, obj2); // false!9 if (!isShallowEqual) {10 logs.push('shallow-fail');11 }1213 // 2. Custom arePropsEqual comparator checks data value:14 function arePropsEqual(prev: { user: { id: number } }, next: { user: { id: number } }) {15 return prev.user.id === next.user.id; // true -> skips render!16 }17 const isCustomEqual = arePropsEqual({ user: obj1 }, { user: obj2 });18 if (isCustomEqual) {19 logs.push('custom-pass');20 }2122 const renderCount = 1; // Only 1 render because custom comparator skipped update!2324 return `${logs.join(' ')} ${renderCount}`;25}2627console.log(simulateReactMemo());
Predict Console & Render Output
shallow-fail custom-pass 1
shallow-pass custom-pass 0
shallow-fail custom-fail 2
custom-pass shallow-fail 1
Execution Trace
Step 1 of 3 (Line 9)Shallow comparison of new object references returns false, triggering render in standard React.memo.
Deep Architectural Explanation
React.memo and Custom Comparators
By default, React.memo(Component) performs a shallow comparison of all props using Object.is.
The Crucial Polarity Difference: arePropsEqual vs shouldComponentUpdate
This is a famous interview trap:
- In Class components:
shouldComponentUpdate(nextProps)returnstrueto RE-RENDER andfalseto skip. - In
React.memo:arePropsEqual(prevProps, nextProps)returnstrueto SKIP RE-RENDER andfalseto re-render!
export const UserCard = React.memo(
function UserCard({ user }: { user: User }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => {
// Return TRUE if passing nextProps produces same result as prevProps (bails out)
return prevProps.user.id === nextProps.user.id;
}
);When to Avoid React.memo
- Components that change on almost every render: The shallow prop comparison runs every time and still ends up rendering, adding net overhead.
- Components whose children change frequently: If props include non-memoized objects or callbacks,
React.memowill always fail its check.
Common Interview Traps & Pitfalls
Inverting the return value of arePropsEqual (returning false when props are equal).
Wrapping every component in React.memo prematurely without measuring with the React Profiler.
Performing deep recursive equality checks in arePropsEqual that take longer than rendering the component.
Senior Interviewer Follow-Up Probes
How does the React Compiler eliminate the need for manual React.memo wrappers?
Why doesn't React.memo prevent re-renders when internal useState or useContext changes?
How do you profile memoization bailout rates in Chrome DevTools?
Frequently Asked Interview Questions
What is the return contract of arePropsEqual in React.memo?
Return true if the component should NOT re-render (props are equal), and return false if the component SHOULD re-render (props changed).
Does React.memo prevent re-renders caused by internal state updates?
No. If a component wrapped in React.memo updates its own internal useState or useReducer, it will still re-render.
Why does passing an inline object prop break React.memo?
Because a new object literal creates a new memory reference on every render, causing Object.is(prev, next) to evaluate to false.
How does React.memo differ from useMemo?
React.memo is a higher-order component for memoizing entire component render trees based on props, while useMemo is a hook for memoizing individual values or functions inside a component.
