Re-renders, React.memo, useMemo, useCallback & Code Splitting
Utilisez les flèches, cliquez ou glissez pour naviguer
1. Comprendre les 3 raisons de re-render
State, parent, context
2. Utiliser React.memo
Éviter les re-renders inutiles
3. Maîtriser useMemo
Mémoriser les calculs coûteux
4. Stabiliser avec useCallback
Callbacks stables en props
5. Implémenter le code splitting
React.lazy + Suspense pour des bundles plus légers
Pourquoi un composant re-rend ?
Les 3 raisons : state, parent, context
React.memo
Empêcher les re-renders quand les props n'ont pas changé
useMemo et useCallback
Stabiliser les valeurs et fonctions passées en props
React.lazy + Suspense
Charger les composants Ă la demande (code splitting)
Live coding
Profiler une app et optimiser les re-renders inutiles
Pourquoi un composant re-rend ?
Les 3 raisons qui déclenchent un re-render
React exécute à nouveau la fonction du composant
Rendu initial
Le composant s'affiche pour la première fois
ReactDOM.createRoot()
Re-render
React ré-exécute le composant
Component()
// → nouveau JSX
💡 Re-render ≠Re-paint du DOM. React compare d'abord le nouveau JSX avec le DOM existant
Quand setState est appelé, le composant re-rend
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
+1
</button>
</div>
);
}
âś… C'est le comportement normal : on VEUT que le composant se mette Ă jour!
Si le parent re-rend, tous les enfants re-rendent automatiquement
Parent
count change
→
Child A
re-render!
→
Child B
re-render!
⚠️ Même si Child A et Child B n'utilisent pas count, ils re-rendent quand même!
Tous les composants qui consomment le context re-rendent
const ThemeContext = createContext('light');
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={theme}>
<Header /> // Re-render
<Sidebar /> // Re-render
<Main /> // Re-render
</ThemeContext.Provider>
);
}
đź’ˇ Tous les consommateurs du context re-rendent, mĂŞme s'ils n'utilisent que d'autres valeurs
1
State change
setState() appelé
2
Parent re-render
Cascade automatique
3
Context change
Provider value change
⚠️ Les re-renders ne sont pas toujours un problème!
Mais quand ils deviennent coûteux, il faut optimiser
React.memo
Empêcher les re-renders quand les props n'ont pas changé
Un HOC (Higher-Order Component) qui "mémoïse" un composant
Sans React.memo
Le composant re-rend Ă chaque fois que le parent re-rend
Performance: ⚠️
Avec React.memo
Le composant ne re-rend que si les props changent
Performance: âś…
const MemoizedChild = React.memo(Child);
function Child({ name }: { name: string }) {
return <div>Hello {name}</div>;
}
export default React.memo(Child);
React compare les props avec Object.is()
Si les props sont identiques → pas de re-render
⚠️ Attention : comparaison superficielle (shallow comparison)
Les objets et tableaux sont comparés par référence, pas par contenu!
âś… Props stables : primitives (string, number, boolean)
function Parent() {
const [count, setCount] = useState(0);
const name = "Alice"; // Primitif stable
return (
<>
<button onClick={() => setCount(count + 1)}>{count}</button>
<Child name={name} /> // âś… Ne re-rend pas!
</>
);
}
🎉 name est une string primitive → comparaison par valeur → stable
❌ Props instables : objets, tableaux, fonctions créés dans le render
function Parent() {
const [count, setCount] = useState(0);
const config = { color: 'blue' }; // ❌ Nouvel objet à chaque render!
return (
<>
<button onClick={() => setCount(count + 1)}>{count}</button>
<Child config={config} /> // ❌ Re-rend à chaque fois!
</>
);
}
⚠️ { color: 'blue' } est un nouvel objet à chaque render → référence différente → React.memo ne peut pas optimiser
❌ Objet inline
<Child config={{ color: 'blue' }} />
Nouvel objet Ă chaque render
âś… Objet stable avec useMemo
const config = useMemo(
() => ({ color: 'blue' }),
[]
);
Même référence entre les renders
đź’ˇ C'est lĂ que useMemo et useCallback deviennent essentiels!
useMemo et useCallback
Stabiliser les valeurs et fonctions passées en props
Retourne une valeur mémorisée qui ne change que si les dépendances changent
const memoizedValue = useMemo(
() => computeExpensiveValue(a, b),
[a, b] // Dépendances
);
() => valeur
Fonction de calcul
[deps]
Tableau de dépendances
Calcul coûteux : filtrer une liste de 10 000 éléments
function ProductList({ products, filter }: Props) {
const visibleProducts = useMemo(() => {
return products.filter(p => p.category === filter);
}, [products, filter]); // Recalcule seulement si products ou filter change
return <ul>{visibleProducts.map(...)}</ul>;
}
✅ Le filtrage ne s'exécute que quand products ou filter change
Créer un objet qui garde la même référence
function Parent() {
const style = useMemo(() => ({
color: 'blue',
fontSize: 16
}), []); // Dépendances vides = jamais recréé
return <Child style={style} />; // âś… Props stable!
}
🎉 style garde la même référence → React.memo fonctionne!
Retourne une fonction mémorisée qui ne change que si les dépendances changent
const memoizedCallback = useCallback(
() => { doSomething(a, b); },
[a, b] // Dépendances
);
đź’ˇ useCallback est un raccourci pour useMemo(() => fn, [deps])
Passer un callback stable Ă un composant React.memo
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('Clicked!');
}, []); // Dépendances vides = fonction stable
return <Child onClick={handleClick} />; // âś… Props stable!
}
🎉 handleClick garde la même référence → Child (React.memo) ne re-rend pas!
useMemo
Mémorise une valeur
✅ Calculs coûteux
âś… Objets/tableaux stables
✅ Résultats de transformations
const value = useMemo(
() => compute(), [deps]
);
useCallback
Mémorise une fonction
✅ Callbacks passés en props
âś… Handlers pour composants memo
✅ Dépendances de useEffect
const fn = useCallback(
() => {}, [deps]
);
React.lazy + Suspense
Charger les composants Ă la demande (code splitting)
Diviser le bundle JavaScript en plusieurs fichiers chargés séparément
Sans code splitting
Un seul gros bundle.js
bundle.js
2.5 MB
⚠️ Chargement initial lent
Avec code splitting
Plusieurs petits bundles
main.js
500 KB
admin.js
300 KB
âś… Chargement initial rapide
Charger un composant dynamiquement avec import()
// Avant : import statique
import Dashboard from './Dashboard';
// Après : import dynamique avec React.lazy
const Dashboard = React.lazy(() => import('./Dashboard'));
⚠️ React.lazy doit être utilisé avec Suspense
Afficher un loader pendant le chargement du composant
const Dashboard = React.lazy(() => import('./Dashboard'));
function App() {
return (
<Suspense fallback={<Loading />}>
<Dashboard />
</Suspense>
);
}
âś… Le composant Loading s'affiche pendant que Dashboard se charge
Pattern recommandé : charger chaque route séparément
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
function App() {
return (
<Suspense fallback={<PageLoader />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
);
}
Live Coding & Profiling
Identifier et optimiser les re-renders inutiles
L'outil indispensable pour mesurer les performances
Installer
Extension Chrome/Firefox : React Developer Tools
Utiliser
Onglet "Profiler" → Record → Interagir → Analyser
đź’ˇ Mesurer avant d'optimiser! Ne pas optimiser "au cas oĂą"
Profil avec React DevTools
Identifier les composants qui re-rendent souvent
Vérifier si le re-render est nécessaire
Les props ont-elles changé ? Le state a-t-il changé ?
Appliquer l'optimisation appropriée
React.memo, useMemo, useCallback selon le cas
Re-profiler pour vérifier l'amélioration
Comparer les temps de render avant/après
Les erreurs à éviter
Ce qu'il ne faut PAS faire
❌ Mauvais
Mettre React.memo partout "au cas oĂą"
// Tout est memoized...
export default React.memo(Header);
export default React.memo(Footer);
export default React.memo(Button);
⚠️ Complexité inutile + overhead de comparaison
âś… Bon
Mesurer d'abord, optimiser ensuite
// 1. Profiler
// 2. Identifier le bottleneck
// 3. Optimiser ce composant
✅ Optimisation ciblée et justifiée
Passer un objet inline Ă un composant React.memo
❌ L'erreur
const Child = memo(({ config }) => ...);
function Parent() {
return (
<Child config={{ color: 'blue' }} />
);
}
{ } = nouvel objet Ă chaque render!
âś… La solution
const config = useMemo(() => ({
color: 'blue'
}), []);
return <Child config={config} />;
Même référence = React.memo fonctionne!
useCallback avec dépendances vides qui capture un state obsolète
❌ L'erreur
const [count, setCount] = useState(0);
const logCount = useCallback(() => {
console.log(count); // Toujours 0!
}, []); // Dépendances vides!
count est "capturé" à la valeur initiale
âś… La solution
const [count, setCount] = useState(0);
const logCount = useCallback(() => {
console.log(count);
}, [count]); // count dans les deps!
Fonction mise Ă jour quand count change
Ne pas confondre : useMemo = valeur, useCallback = fonction
// useCallback est un raccourci pour useMemo
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
// Équivalent à :
const handleClick = useMemo(() => {
return () => {
console.log('clicked');
};
}, []);
đź’ˇ Retenez : useCallback(fn, deps) = useMemo(() => fn, deps)
1. Les 3 raisons de re-render
State change, parent re-render, context change
2. React.memo ne fonctionne qu'avec des props stables
D'oĂą l'importance de useMemo et useCallback
3. useMemo pour les calculs, useCallback pour les callbacks
Les deux stabilisent les valeurs passées en props
4. React.lazy pour le code splitting par route
Chaque page se charge séparément
⚠️ Mesurer avant d'optimiser!
React DevTools Profiler est votre ami
1. Identifier les re-renders
Utiliser React DevTools Profiler sur une app existante
2. Optimiser avec React.memo
Wrapper un composant et vérifier qu'il ne re-rend pas
3. Stabiliser les props
Utiliser useMemo pour un objet, useCallback pour une fonction
4. Implémenter le code splitting
Convertir les imports statiques en React.lazy avec Suspense
L'optimisation est un art : mesurer, optimiser, vérifier
À retenir : Ne pas optimiser prématurément!
Utilisez React DevTools Profiler pour identifier les vrais problèmes