Skip to content
GeeksSmith
advanced 10 min read

React Performance Optimization & Profiling Lab

React performance optimization focuses on eliminating unnecessary re-renders, breaking long JavaScript execution tasks into concurrent slices (startTransition), and reducing commit work via component composition.

Why it matters in interviews

Prematurely wrapping everything in useMemo and useCallback adds memory overhead and cognitive noise without solving the root cause of render bottlenecks. Top interviewers test whether you can profile and architect performant component trees before reaching for memoization.

Visual & Interactive Explanation

Composition (Children Pattern) vs React.memo

Interactive Comparison

Children Composition

Zero memory overhead

Architectural
MechanismPass expensive component as children prop
Memory Cost0 bytes (No cache comparisons)
ComplexityClean declarative JSX structure
Strengths
  • No props comparison overhead on each render
  • Naturally enforces clean component boundaries
Trade-offs
  • Requires architecting component hierarchy upfront

React.memo()

Shallow props comparison

Memoization
MechanismCompares prevProps with nextProps shallowly
Memory CostSmall cache reference overhead
PrerequisiteAll object/function props must be memoized
Strengths
  • Can be applied to existing isolated components
Trade-offs
  • Fails silently if an unmemoized inline function is passed as prop

startTransition

Concurrent priority slicing

Concurrent
MechanismYields main thread to user inputs
INP ProtectionKeeps typing and click inputs at 120Hz
Loading StateProvides isPending boolean indicator
Strengths
  • Prevents browser UI freezes on heavy list rendering
Trade-offs
  • Still executes work on main thread; requires Concurrent React

Code Examples & Implementation

Optimizing Performance without memo() via Children Composition
// ❌ SLOW: ColorPicker state change causes ExpensiveChart to re-render every time
function SlowDashboard() {
  const [color, setColor] = useState('blue');
  return (
    <div style={{ color }}>
      <input value={color} onChange={(e) => setColor(e.target.value)} />
      <ExpensiveChart data={100000} /> {/* Re-renders on every keystroke! */}
    </div>
  );
}

// ✅ FAST: ColorPicker wraps children. ExpensiveChart is passed as JSX prop and NEVER re-renders!
function ColorContainer({ children }: { children: React.ReactNode }) {
  const [color, setColor] = useState('blue');
  return (
    <div style={{ color }}>
      <input value={color} onChange={(e) => setColor(e.target.value)} />
      {children} {/* Same JSX element reference; React skips re-rendering it! */}
    </div>
  );
}

export function FastDashboard() {
  return (
    <ColorContainer>
      <ExpensiveChart data={100000} />
    </ColorContainer>
  );
}

Interview-Ready Answers

Optimize React by profiling first, colocating state near its consumer, utilizing component composition (children pattern) to skip subtree re-renders, and using startTransition for non-blocking concurrent rendering.

Official Documentation & Specifications

Follow-up Questions & Deep Dives

🎯 Code Output — What Does This Print?

Question 1hard
function Wrapper({ children }: { children: React.ReactNode }) {
  const [count, setCount] = useState(0);
  console.log("Wrapper render");
  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount((c) => c + 1)}>+</button>
      {children}
    </div>
  );
}

function Heavy() {
  console.log("Heavy render");
  return <div>Heavy component</div>;
}

// Usage:
// <Wrapper><Heavy /></Wrapper>
// User clicks + button. What logs?
Question 2tricky
const MemoChild = React.memo(({ config }: { config: { theme: string } }) => {
  console.log("MemoChild render");
  return <p>{config.theme}</p>;
});

function Parent() {
  const [count, setCount] = useState(0);
  console.log("Parent render");
  return (
    <>
      <p>{count}</p>
      <MemoChild config={{ theme: "dark" }} />
      <button onClick={() => setCount((c) => c + 1)}>+</button>
    </>
  );
}
// User clicks + button. What logs?

Common Mistakes & Anti-Patterns

  • Wrapping simple components in React.memo whose props comparison takes more time than re-rendering the component itself.
  • Passing non-memoized inline objects (e.g. style={{ color: 'red' }}) or inline arrow functions to React.memo components.
  • Placing state in a top-level root component instead of pushing it down to the leaf component that actually uses it.

Real-World Architectural Scenario

A search input filters a list of 5,000 items, causing keystroke lag and stutter. Profiler shows a 120ms render commit on every keypress. How do you fix this?

Rate Your Readiness

Rate how comfortably you can explain this in an interview to update your global readiness gauge:

Rate your confidence: