Performance
Optimiser les rendus avec memo, useMemo, useCallback
Le concept : La beauté de React est de se rafraîchir à chaque changement de State. Mais face à des listes géantes ou des calculs lourds, ce re-rendu automatique peut ralentir l'application.
C'est là qu'interviennent les outils de mémorisation. React.memo agit comme un videur de boîte de nuit : "Ce composant ne se re-dessine QUE si l'on change ses Props exactes". useMemo et useCallback font la même chose, mais respectivement pour un calcul lourd ou une fonction, en les mettant en cache entre deux rendus.
💡 Comprendre le cycle de rendu React
Par défaut, quand un composant parent se re-rend, tous ses enfants se re-rendent aussi, même si leurs props n'ont pas changé. React fait ça parce que c'est généralement rapide — le Virtual DOM diffing est efficace.
Mais dans certains cas (grandes listes, calculs coûteux, descendance profonde), il faut optimiser. Les 3 outils principaux :
React.memo— Mémoiser un composant (éviter le re-rendu si les props n'ont pas changé).useMemo— Mémoiser la valeur d'un calcul coûteux.useCallback— Mémoiser une fonction (pour éviter de recréer une référence à chaque rendu).
memo/useMemo. Une sur-optimisation rend le code plus complexe sans bénéfice réel.🧠 React.memo — Mémoisation de composant
// ❌ ChartBar se re-rend à chaque rendu de App // même si ses props (value, label) n'ont pas changé function App() { const [count, setCount] = useState(0); return ( <> <button onClick={() => setCount(c => c+1)}>+</button> // ↓ Re-rendu inutile si value et label ne changent pas <HeavyChart data={bigDataset} /> </> ); }
// ✅ HeavyChart ne re-rend que si ses props changent const HeavyChart = React.memo(function HeavyChart({ data }) { // Rendu coûteux return <canvas id="chart" />; }); // Comparaison personnalisée (deep equal) const MemoList = React.memo(MyList, (prevProps, nextProps) => { // Retourner true = pas de re-rendu return prevProps.items.length === nextProps.items.length; });
🔢 useMemo — Mémoiser un calcul coûteux
import { useMemo } from 'react'; function ProductList({ products, filter }) { // ❌ Recalculé à chaque rendu // const filtered = products.filter(p => p.category === filter); // ✅ Recalculé uniquement si products ou filter change const filtered = useMemo(() => { return products .filter(p => p.category === filter) .sort((a, b) => a.price - b.price); }, [products, filter]); return filtered.map(p => <ProductCard key={p.id} {... p} />); }
import { useCallback } from 'react'; function TodoList({ items }) { const [selected, setSelected] = useState(null); // ❌ Nouvelle fonction à chaque rendu → memo échoue // const handleSelect = (id) => setSelected(id); // ✅ Même référence de fonction entre les rendus const handleSelect = useCallback((id) => { setSelected(id); }, []); // Stable car setSelected l'est toujours return items.map(item => ( <TodoItem key={item.id} onSelect={handleSelect} /> )); } const TodoItem = React.memo(({ onSelect }) => { ... });
📦 Code Splitting : lazy() & Suspense
import { lazy, Suspense } from 'react'; // Chargement à la demande (bundle splitting Vite/Webpack) const Dashboard = lazy(() => import('./pages/Dashboard')); const HeavyChart = lazy(() => import('./components/HeavyChart')); // Suspense affiche un fallback pendant le chargement function App() { return ( <Suspense fallback={<div>Chargement...</div>}> <Routes> <Route path="/dashboard" element={<Dashboard />} /> </Routes> </Suspense> ); } // Impact : au lieu d'un bundle de 2MB, l'utilisateur charge // un bundle initial de ~300KB, puis les pages à la demande
🔍 Quand optimiser ?
- ✅ Listes de 100+ éléments avec re-rendus fréquents
- ✅ Calculs mathématiques lourds (tri, filtrage complexe)
- ✅ Callback passé à des enfants mémoïsés (React.memo)
- ✅ Code splitting pour les routes et composants lourds
- ❌ Composants simples (le memo coûte aussi quelque chose)
- ❌ Prématurément, sans mesure préalable
🛠️ Outils de diagnostic
- 🔧 React DevTools Profiler — Visualise quel composant se rend et combien de temps.
- ⚙️ why-did-you-render — Détecte les re-rendus inutiles en dev.
- 📊 Lighthouse — Métriques de performance globales.
- 🔍 React DevTools → Components → Mettre en surbrillance les composants qui re-rendent.
react-window ou @tanstack/react-virtual.