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
Visual & Interactive Explanation
Composition (Children Pattern) vs React.memo
Children Composition
Zero memory overhead
- •No props comparison overhead on each render
- •Naturally enforces clean component boundaries
- •Requires architecting component hierarchy upfront
React.memo()
Shallow props comparison
- •Can be applied to existing isolated components
- •Fails silently if an unmemoized inline function is passed as prop
startTransition
Concurrent priority slicing
- •Prevents browser UI freezes on heavy list rendering
- •Still executes work on main thread; requires Concurrent React
Code Examples & Implementation
// ❌ 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?
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?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: