Master React performance optimization by eliminating unnecessary component re-renders, caching expensive computations, and maintaining stable function references.
1. How React Re-renders Components
2. React.memo: Component-Level Memoization
Wrap a functional component with React.memo to skip re-rendering if its incoming props are shallowly equal:
⊞Code Example
import React, { memo } from 'react';
// This component only re-renders if 'title' or 'count' value actually changes
export const MetricCard = memo(function MetricCard({ title, count }) {
console.log('Rendering MetricCard:', title);
return (
<div className="p-4 bg-slate-900 border border-slate-800 rounded-xl">
<span className="text-xs text-slate-400 font-bold uppercase">{title}</span>
<p className="text-2xl font-black text-[#04AA6D]">{count}</p>
</div>
);
});
3. useMemo: Caching Expensive Computations
Use useMemo to cache the calculated result of expensive operations (e.g. filtering 10,000 items, calculating statistics) between renders:
⊞Code Example
import React, { useState, useMemo } from 'react';
export function TransactionAnalytics({ transactions }) {
const [filter, setFilter] = useState('all');
// Cache filtered calculation: only re-calculates when transactions or filter change
const totalVolume = useMemo(() => {
console.log('Calculating total transaction volume...');
return transactions
.filter((item) => filter === 'all' || item.type === filter)
.reduce((sum, item) => sum + item.amount, 0);
}, [transactions, filter]);
return (
<div className="p-6 bg-[#0c1328] border border-slate-800 rounded-2xl">
<h2 className="text-xl font-bold text-white">Total Volume: ${totalVolume.toLocaleString()}</h2>
<div className="flex gap-2 mt-4">
<button onClick={() => setFilter('all')} className="px-3 py-1 bg-slate-800 text-white rounded">All</button>
<button onClick={() => setFilter('crypto')} className="px-3 py-1 bg-slate-800 text-white rounded">Crypto</button>
</div>
</div>
);
}
4. useCallback: Preserving Function References
Use useCallback to memoize a callback function instance between renders, preventing memoized child components from re-rendering:
⊞Code Example
import React, { useState, useCallback } from 'react';
import { MetricCard } from './MetricCard';
export function PerformanceDashboard() {
const [counter, setCounter] = useState(0);
// Stable callback reference: function identity does not change on counter updates
const handleExport = useCallback((format) => {
console.log('Exporting analytics report in format:', format);
}, []);
return (
<div className="space-y-4">
<button
onClick={() => setCounter((c) => c + 1)}
className="px-4 py-2 bg-[#04AA6D] text-white font-bold rounded-lg"
>
Increment: {counter}
</button>
{/* MetricCard will NOT re-render when counter increments */}
<MetricCard title="Completed Lessons" count={counter} onExport={handleExport} />
</div>
);
}
