M5: Hooks & Performance
Optimize your React app with custom hooks, useMemo, useCallback, and React.memo.
Master advanced React patterns and optimize performance with custom hooks and memoization! ⚡
Overview
Your app works great, but as it grows, you might notice:
- Repeated logic across components
- Slow filters or calculations
- Unnecessary re-renders
- Components that could be more efficient
This module teaches performance optimization and code reusability - two essential skills for building production-quality React applications.
You'll learn to extract logic into custom hooks, optimize expensive operations, and prevent unnecessary re-renders using React's performance tools.
What You'll Build
🪝 Custom Hooks
Extract data fetching logic into reusable useFetch hook
🧮 Optimized Calculations
Use useMemo to cache expensive filter operations
🔄 Stable Functions
Memoize callbacks with useCallback to prevent child re-renders
⚡ Memoized Components
Wrap components with React.memo to skip unnecessary renders
📊 Performance Profiling
Use React DevTools to measure and improve performance
Key Concepts
Custom Hooks
Extract reusable logic into custom hooks:
// Before: Repeated fetch logic in every component
function HomePage() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
// Fetch logic...
}, []);
}
// After: Reusable custom hook
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
// Fetch logic...
}, [url]);
return { data, loading, error };
}
// Usage
function HomePage() {
const { data, loading, error } = useFetch('/api/listings');
}useMemo
Cache expensive calculations:
// Without useMemo - recalculates every render
function SearchPage({ listings, filters }) {
const filtered = listings.filter(item =>
item.price < filters.maxPrice &&
item.guests >= filters.guests
);
// If parent re-renders, this filtering runs again unnecessarily
}
// With useMemo - only recalculates when dependencies change
function SearchPage({ listings, filters }) {
const filtered = useMemo(() =>
listings.filter(item =>
item.price < filters.maxPrice &&
item.guests >= filters.guests
),
[listings, filters] // Only re-run when these change
);
}useCallback
Memoize functions to prevent child re-renders:
// Without useCallback - new function every render
function Parent() {
const handleClick = () => {
console.log('clicked');
};
return <MemoizedChild onClick={handleClick} />;
// Child re-renders even if nothing changed
}
// With useCallback - stable function reference
function Parent() {
const handleClick = useCallback(() => {
console.log('clicked');
}, []); // Function stays the same
return <MemoizedChild onClick={handleClick} />;
// Child doesn't re-render unnecessarily
}React.memo
Prevent component re-renders when props haven't changed:
// Without memo - re-renders every time parent renders
function PropertyCard({ property }) {
return <div>{property.name}</div>;
}
// With memo - only re-renders if 'property' changes
const PropertyCard = React.memo(function PropertyCard({ property }) {
return <div>{property.name}</div>;
});Learning Objectives
Module Roadmap
| Lesson | What You'll Optimize | Tools/Hooks Used |
|---|---|---|
| 1-3 | Data fetching | Custom hooks (useFetch) |
| 4-5 | Filter calculations | useMemo |
| 6 | Event handlers | useCallback |
| 7 | Component renders | React.memo |
| 8-9 | Performance analysis | React DevTools Profiler |
| 10 | Final review | Best practices |
Prerequisites
Before starting Module 5, ensure:
- ✅ Completed Module 4
- ✅ Understand useEffect and useState
- ✅ Know how to fetch data from APIs
- ✅ Comfortable with React components and props
- ✅ Have React DevTools browser extension installed
When to Optimize
⚠️ Don't optimize prematurely!
Optimization adds complexity. Only optimize when you have:
- Measured performance issues using profiler
- Identified the bottleneck causing slowness
- A clear performance goal to achieve
Premature optimization is the root of all evil - Donald Knuth
✅ Optimize when:
- Expensive calculations in render (complex filtering, sorting)
- Large lists causing lag
- Components re-rendering unnecessarily
- React DevTools Profiler shows high render times
- Users report sluggish interactions
Performance Checklist
When to use useMemo
✅ Filtering or sorting large arrays
✅ Complex calculations in render
✅ Derived state from expensive operations
❌ Simple values or primitives
❌ Calculations that are already fast
When to use useCallback
✅ Functions passed to memoized child components
✅ Functions in useEffect dependencies
✅ Functions passed to many children
❌ Functions only used in the current component
❌ Event handlers not passed as props
When to use React.memo
✅ Components that re-render often with same props
✅ Components with expensive render logic
✅ List items rendered in .map()
❌ Components that rarely re-render
❌ Components with constantly changing props
Custom Hooks Best Practices
Naming convention:
// ✅ Always start with "use"
function useFetch() {}
function useLocalStorage() {}
function useDebounce() {}
// ❌ Don't skip "use" prefix
function fetchData() {}
function localStorage() {}Common custom hooks:
// Data fetching
useFetch(url)
useQuery(query)
// Form handling
useForm(initialValues)
useField(name)
// Browser APIs
useLocalStorage(key, initialValue)
useMediaQuery(query)
useClickOutside(ref, handler)
// Utilities
useDebounce(value, delay)
useToggle(initialValue)
usePrevious(value)React DevTools Profiler
How to use:
- Install React DevTools browser extension
- Open DevTools → Profiler tab
- Click Record button
- Interact with your app
- Stop recording
- Analyze flame graph
What to look for:
🟢 Green bars = Fast render (< 5ms)
🟡 Yellow bars = Moderate (5-15ms)
🔴 Red bars = Slow (> 15ms) - Optimize these!Optimization wins:
Before:
HomePage: 45ms (with 100 properties)
PropertyCard × 100: 2ms each = 200ms total
Total: 245ms 🔴
After optimization:
HomePage: 15ms (filtered cached with useMemo)
PropertyCard × 100: memoized, skips re-render
Total: 15ms 🟢
16x faster! ⚡Common Performance Pitfalls
Pitfall 1: Unnecessary State
// ❌ BAD - derived state
const [items, setItems] = useState([]);
const [count, setCount] = useState(0);
useEffect(() => {
setCount(items.length); // Unnecessary state
}, [items]);
// ✅ GOOD - calculate on demand
const [items, setItems] = useState([]);
const count = items.length; // Just calculate it!Pitfall 2: Inline Objects/Arrays
// ❌ BAD - new object every render
<Component style={{ color: 'red' }} />
// ✅ GOOD - stable reference
const style = { color: 'red' };
<Component style={style} />
// Or use useMemo for dynamic values
const style = useMemo(() => ({
color: theme === 'dark' ? 'white' : 'black'
}), [theme]);Pitfall 3: Large Lists Without Keys
// ❌ BAD - no keys, React re-renders everything
{items.map(item => <Card>{item.name}</Card>)}
// ✅ GOOD - stable keys, React only updates changed items
{items.map(item => <Card key={item.id}>{item.name}</Card>)}Before vs After This Module
Before Module 5:
function HomePage() {
const [listings, setListings] = useState([]);
const [loading, setLoading] = useState(true);
const [filters, setFilters] = useState({});
// Repeated fetch logic
useEffect(() => {
fetch('/api/listings')
.then(res => res.json())
.then(data => {
setListings(data);
setLoading(false);
});
}, []);
// Re-filters every render (slow!)
const filtered = listings.filter(item =>
item.price < filters.maxPrice
);
return <PropertyList properties={filtered} />;
}After Module 5:
function HomePage() {
// Reusable custom hook
const { data: listings, loading } = useFetch('/api/listings');
const [filters, setFilters] = useState({});
// Cached filtering (only recalculates when needed)
const filtered = useMemo(() =>
listings?.filter(item =>
item.price < filters.maxPrice
) ?? [],
[listings, filters]
);
return <PropertyList properties={filtered} />;
}
// Memoized component (skips re-renders)
const PropertyList = React.memo(function PropertyList({ properties }) {
return properties.map(p => <PropertyCard key={p.id} property={p} />);
});Results:
- ✅ Less code duplication
- ✅ Better performance
- ✅ Easier to maintain
- ✅ Reusable logic
Time Estimate
⏱️ 3-4 hours to complete all 10 lessons
- Lessons 1-3 (Custom hooks): ~1.5 hours
- Lessons 4-5 (useMemo): ~45 minutes
- Lessons 6-7 (useCallback & React.memo): ~45 minutes
- Lessons 8-10 (Profiling & review): ~1 hour
What Makes This Module Important
Professional React developers must:
- Write reusable, maintainable code
- Optimize performance for production
- Use profiling tools to measure impact
- Know when and how to optimize
After this module, you'll:
- Extract custom hooks confidently
- Optimize intelligently (not prematurely)
- Profile and debug performance issues
- Write production-quality React code
Let's Begin!
Ready to level up your React skills with custom hooks and performance optimization? Let's start by creating a reusable useFetch hook!