React.memo
React.memo
React.memo prevents unnecessary re-renders by memoizing components.
Basic Usage
const MemoizedComponent = React.memo(function Component({ prop }) {
console.log("Rendered");
return <div>{prop}</div>
});
// Only re-renders when props change (shallow comparison)
When It Helps
// Parent re-renders, but Child only re-renders if props change
function Parent() {
const [count, setCount] = useState(0);
const [name, setName] = useState("Alice");
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<MemoizedChild name={name} /> // Only re-renders when name changes
</div>
);
}
const MemoizedChild = React.memo(function Child({ name }) {
console.log("Child rendered");
return <p>{name}</p>;
});
Custom Comparison
const MemoizedComponent = React.memo(
function Component({ user, onSelect }) {
return (
<div onClick={() => onSelect(user.id)}>
{user.name}
</div>
);
},
(prevProps, nextProps) => {
// Custom comparison logic
return prevProps.user.id === nextProps.user.id;
}
);
Common Mistakes
// Bad: Inline function creates new reference every render
<Child onClick={() => handleClick(id)} />
// Good: useCallback for stable reference
const handleClick = useCallback((id) => {
// ...
}, []);
<Child onClick={handleClick} />
// Bad: Object literal creates new reference every render
<Child style={{ color: "red" }} />
// Good: Constant object
const style = { color: "red" };
<Child style={style} />
Code Splitting
Code Splitting
Code splitting loads code only when needed.
React.lazy
import { lazy, Suspense } from "react";
// Lazy load components
const Dashboard = lazy(() => import("./Dashboard"));
const Settings = lazy(() => import("./Settings"));
function App() {
const [page, setPage] = useState("dashboard");
return (
<div>
<nav>
<button onClick={() => setPage("dashboard")}>Dashboard</button>
<button onClick={() => setPage("settings")}>Settings</button>
</nav>
<Suspense fallback={<Loading />}>
{page === "dashboard" && <Dashboard />}
{page === "settings" && <Settings />}
</Suspense>
</div>
);
}
Route-Based Splitting
import { lazy, Suspense } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
const Home = lazy(() => import("./pages/Home"));
const About = lazy(() => import("./pages/About"));
const Contact = lazy(() => import("./pages/Contact"));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
Error Boundaries
import { Component } from "react";
class ErrorBoundary extends Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
// Usage with lazy loading
<ErrorBoundary>
<Suspense fallback={<Loading />}>
<LazyComponent />
</Suspense>
</ErrorBoundary>
Benefits
- Smaller initial bundle
- Faster load time
- Load on demand
- Better caching
Profiling
Profiling
React DevTools Profiler
- Install React DevTools
- Go to Profiler tab
- Click Record
- Perform actions
- Analyze results
What to Look For
- Unused re-renders: Components re-rendering without changes
- Slow renders: Components taking too long to render
- Memory leaks: Components not cleaning up
Performance Patterns
// Bad: Re-renders entire list
function TodoList({ todos }) {
return (
<ul>
{todos.map((todo) => (
<TodoItem key={todo.id} todo={todo} />
))}
</ul>
);
}
// Good: Memoize individual items
const TodoItem = React.memo(function TodoItem({ todo }) {
return <li>{todo.text}</li>;
});
// Good: Virtualize large lists
import { FixedSizeList } from "react-window";
function VirtualizedList({ items }) {
return (
<FixedSizeList
height={500}
itemCount={items.length}
itemSize={35}
>
{({ index, style }) => (
<div style={style}>{items[index].name}</div>
)}
</FixedSizeList>
);
}
useMemo and useCallback
// Memoize expensive computations
const sortedItems = useMemo(
() => [...items].sort((a, b) => a.name.localeCompare(b.name)),
[items]
);
// Memoize callbacks for memoized children
const handleSelect = useCallback((id) => {
setSelected(id);
}, []);
// Use with React.memo
const Child = React.memo(({ onSelect }) => ...);
<Child onSelect={handleSelect} />
Bundle Analysis
# Install bundle analyzer
npm install --save-dev webpack-bundle-analyzer
# Analyze bundle
npx webpack-bundle-analyzer stats.json
Performance Checklist
- Use React.memo for pure components
- Memoize expensive computations
- Use useCallback for stable callbacks
- Code split with React.lazy
- Virtualize large lists
- Avoid inline functions in JSX
- Profile with React DevTools
Practice Problems
Create a reusable React component implementing React Performance. Include proper state management and accessibility.
Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where neededWrite unit and integration tests for React Performance using React Testing Library.
Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility testsOptimize React Performance for performance. Consider memoization, code splitting, and bundle size.
Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysisQuiz
1. What does React.memo do?
2. What is code splitting?
3. How do you lazy load a component?
4. What is the purpose of Suspense?
Flashcards
Question
What does React.memo do?
Click to reveal answer
Answer
Prevents unnecessary re-renders by memoizing components
Question
How do you lazy load a component?
Click to reveal answer
Answer
lazy(() => import('./Component'))
Question
What is code splitting?
Click to reveal answer
Answer
Loading code only when needed to reduce bundle size
Question
What does Suspense do?
Click to reveal answer
Answer
Shows fallback UI while lazy components load
Question
What is React Performance?
Click to reveal answer
Answer
React Performance is a key concept in frontend development.
Revision Notes
Key Takeaways
- 1.React.memo prevents unnecessary re-renders
- 2.Code splitting reduces initial bundle size
- 3.Suspense handles loading states
- 4.Memoize expensive computations
- 5.Profile with React DevTools
Interview Tips
- •Explain React.memo and when to use it
- •Show how to implement code splitting
- •Discuss performance optimization strategies
Cheat Sheet
Cheat Sheet
React.memo
const Child = React.memo(({ prop }) => ...);
// Only re-renders when props change
Lazy Loading
const Lazy = lazy(() => import('./Component'));
<Suspense fallback={<Loading />}>
<Lazy />
</Suspense>
useMemo
const result = useMemo(() => expensive(data), [data]);
useCallback
const fn = useCallback(() => doStuff(a, b), [a, b]);
Performance Tips
- Use React.memo for pure components
- Memoize expensive computations
- Code split with lazy loading
- Virtualize large lists
- Avoid inline functions