What Is a Pure Component in React?
Advertisement
🧩 Scenario
Architecture Walkthrough
What "Pure" Means in React
A pure component is a component that always produces the same output for the same inputs, with no side effects during rendering. The render function is a pure function: given identical props and state, it returns identical JSX. React exploits this property to optimize rendering: if the inputs have not changed, the output cannot have changed, and React can skip calling the component function.
The optimization works by checking whether props (and state, for class components) are equal to the previous render before deciding to re-render. If they are equal, React reuses the last render output without calling the component function.
Shallow Equality and Its Limits
Both PureComponent and React.memo perform shallow equality using Object.is on each prop (and state, for PureComponent). For primitives (strings, numbers, booleans), Object.is behaves like === and the comparison is accurate. For objects and arrays, Object.is compares references, not contents. An object literal { color: 'red' } created in JSX is a new object on every render, so even if the content is identical, the reference differs and the comparison reports inequality.
Functions are the most common trap. An inline arrow function in a parent's render body is a new function instance on every render. Passing onAddToCart={() => dispatch(addItem(id))} directly to a memoized child means the child re-renders on every parent render despite the memo wrapper, because the function reference is new every time. The fix is useCallback to stabilize the function reference.
When Not to Apply Memoization
React.memo is not free. The shallow equality check runs on every parent render, comparing each prop. For cheap components that render quickly anyway, the cost of the check may exceed the cost of the re-render it prevents. Adding React.memo to every component by default adds cognitive overhead and can slow things down on component-heavy pages. The correct workflow is to profile first, identify components where unnecessary re-renders cause measurable slowness, and apply React.memo only there.
Key Code Explained
import React, { PureComponent, memo, useCallback } from 'react';
// Class component: PureComponent overrides shouldComponentUpdate
// with shallow comparison of props and state
class UserCard extends PureComponent<{ name: string; age: number }> {
render() {
console.log('UserCard render:', this.props.name);
return (
<div>
<h3>{this.props.name}</h3>
<p>Age: {this.props.age}</p>
</div>
);
}
}
// Equivalent regular Component — re-renders on every parent render
class RegularUserCard extends React.Component<{ name: string; age: number }> {
render() {
return <div><h3>{this.props.name}</h3></div>;
}
}
// Function component: React.memo wraps with shallow prop comparison
const ProductCard = memo(function ProductCard({
product,
onAddToCart,
}: {
product: Product;
onAddToCart: (id: string) => void;
}) {
console.log('ProductCard render:', product.name);
return (
<div className="card">
<h3>{product.name}</h3>
<p>${product.price}</p>
<button onClick={() => onAddToCart(product.id)}>Add to cart</button>
</div>
);
});
// Parent: must stabilize function and object references for memo to work
function ProductGrid({ products }: { products: Product[] }) {
const [cartCount, setCartCount] = React.useState(0);
// Without useCallback: new function reference on every render
// ProductCard re-renders every time cartCount changes — memo is bypassed
const handleAddToCart = useCallback((id: string) => {
// Add to cart logic
setCartCount((prev) => prev + 1);
}, []); // stable reference — same function across all renders
return (
<div>
<p>Cart: {cartCount} items</p>
{products.map((product) => (
<ProductCard
key={product.id}
product={product} // product from a stable array — reference stable
onAddToCart={handleAddToCart} // stable via useCallback
/>
))}
</div>
);
}
// What React.memo does NOT prevent: context-triggered re-renders
const ThemeContext = React.createContext({ color: 'blue' });
const ThemedButton = memo(function ThemedButton({ label }: { label: string }) {
const theme = React.useContext(ThemeContext);
// This component re-renders whenever ThemeContext value changes
// React.memo wrapping does not prevent context-triggered renders
return <button style={{ color: theme.color }}>{label}</button>;
});
The console.log inside ProductCard is the practical debugging tool: it shows whether React.memo is actually preventing renders or if a prop reference issue is defeating it. A component that logs "render" on every parent render despite being wrapped in memo has an unstable prop reference. Check each prop passed to it.
Tradeoffs
| Approach | Re-render behavior | What prevents re-render | Caveats |
|---|---|---|---|
| Regular component | On every parent render | Nothing | Simple; fine for cheap components |
| PureComponent / React.memo | Skipped when props shallowly equal | Shallow equality check | Object/function props must be stable references |
| Custom arePropsEqual comparator | Skipped when custom check returns true | Your logic | Easy to write incorrectly; maintenance burden |
What Interviewers Actually Check
- Whether you know both
PureComponent(class) andReact.memo(function) and the relationship between them - Whether you can explain shallow equality and predict which prop types will and will not benefit from it
- Whether you know inline object and function props silently bypass memoization
- Whether you know
React.memodoes not prevent context-triggered re-renders - Whether you know to profile first rather than apply memoization speculatively
Follow-Up Questions
- What is the second argument to
React.memoand when would you use it? - A component using
React.memowith no props still re-renders. What causes this? PureComponentperforms shallow comparison of both props and state.React.memocompares props only. How do you prevent re-renders for a function component based on internal state that did not change?- You add
React.memoto a list of 200 items. The parent re-renders on every keystroke. Describe the computation React performs and when this optimization pays off versus when it does not. - How does Zustand or Redux (with
useSelector) avoid re-rendering components when the selected slice of state has not changed?
Common Candidate Mistakes
- Saying
PureComponentorReact.memodo deep equality — both useObject.isshallow comparison - Passing inline object literals or arrow functions as props to a memoized component — new reference every render defeats the check silently
- Not using
useCallbackon function props passed to memoized children - Thinking
React.memoprevents context-triggered re-renders — it does not; a component consuming context re-renders when context changes regardless of memo - Applying
React.memoto every component without profiling — adds comparison overhead for no benefit when components re-render due to unstable props anyway
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you implement
PureComponentandReact.memoand explain what each does? - Can you explain shallow equality and why inline objects and functions bypass it?
- Can you explain why
useCallbackis required for function props passed to memoized components? - Can you explain that
React.memodoes not prevent context-triggered re-renders? - Can you describe the correct workflow: profile first, then apply targeted memoization?
Summary
A pure component skips re-rendering when its inputs (props and state) are equal to the previous render. PureComponent provides this for class components via a shallow comparison of all props and state in shouldComponentUpdate. React.memo provides the same for function components via a shallow prop comparison. Both use Object.is per property, which means primitive values compare correctly but objects and functions compare by reference. Inline object literals and arrow functions create new references on every parent render, silently defeating memoization. Stabilize function references with useCallback and object/array references with useMemo on the parent side. React.memo does not prevent re-renders triggered by useContext — context changes always re-render consuming components. Profile before applying memoization; the comparison cost can exceed the render cost for cheap components.
What is the difference between PureComponent and React.memo?
PureComponent is for class components. It overrides shouldComponentUpdate to perform a shallow comparison of all props and state. React.memo is for function components. It performs a shallow comparison of props only. They achieve the same optimization goal via different APIs.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement