What Is a Pure Component in React?

Intermediate8 min interview
Skills tested:
Explaining what "pure" means in the context of React componentsKnowing that PureComponent performs shallow comparison of props and stateKnowing that React.memo performs shallow comparison of props onlyExplaining the shallow equality trap: inline objects and functions always produce new referencesKnowing that memoization has its own cost and should not be applied speculatively

Advertisement

🧩 Scenario

Pure components are a classic React interview topic. Interviewers look for whether you understand the shallow equality mechanism, the reference trap that defeats it, and whether you apply memoization thoughtfully rather than everywhere.

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

ApproachRe-render behaviorWhat prevents re-renderCaveats
Regular componentOn every parent renderNothingSimple; fine for cheap components
PureComponent / React.memoSkipped when props shallowly equalShallow equality checkObject/function props must be stable references
Custom arePropsEqual comparatorSkipped when custom check returns trueYour logicEasy to write incorrectly; maintenance burden

What Interviewers Actually Check

  • Whether you know both PureComponent (class) and React.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.memo does not prevent context-triggered re-renders
  • Whether you know to profile first rather than apply memoization speculatively

Follow-Up Questions

  1. What is the second argument to React.memo and when would you use it?
  2. A component using React.memo with no props still re-renders. What causes this?
  3. PureComponent performs shallow comparison of both props and state. React.memo compares props only. How do you prevent re-renders for a function component based on internal state that did not change?
  4. You add React.memo to 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.
  5. How does Zustand or Redux (with useSelector) avoid re-rendering components when the selected slice of state has not changed?

Common Candidate Mistakes

  • Saying PureComponent or React.memo do deep equality — both use Object.is shallow comparison
  • Passing inline object literals or arrow functions as props to a memoized component — new reference every render defeats the check silently
  • Not using useCallback on function props passed to memoized children
  • Thinking React.memo prevents context-triggered re-renders — it does not; a component consuming context re-renders when context changes regardless of memo
  • Applying React.memo to 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 PureComponent and React.memo and explain what each does?
  • Can you explain shallow equality and why inline objects and functions bypass it?
  • Can you explain why useCallback is required for function props passed to memoized components?
  • Can you explain that React.memo does 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.

Frequently Asked Questions

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