Bonjour ! Bienvenue dans cette nouvelle leçon du module sur la performance.
Dans la leçon précédente, nous avons vu comment faire communiquer une interface utilisateur 2D avec une scène 3D, une étape clé pour créer des applications interactives. Maintenant que vous savez assembler des scènes riches et dynamiques, une nouvelle question se pose : comment s'assurer que tout reste fluide et performant ? Une scène 3D, même simple, peut rapidement consommer beaucoup de ressources si elle n'est pas optimisée.
L'objectif de cette leçon est d'aborder la première ligne de défense contre les problèmes de performance en React : prévenir les re-calculs et les re-rendus inutiles. Vous apprendrez à utiliser deux outils fondamentaux de l'écosystème React, React.memo et useMemo, qui sont absolument essentiels pour optimiser des applications React Three Fiber.
1. Comprendre le "Rendu" et son Coût
Avant d'optimiser, il faut comprendre ce que l'on optimise. En React, le terme "rendu" (ou render) signifie que React exécute le code de votre fonction de composant pour savoir à quoi doit ressembler l'interface. Un re-rendu n'implique pas forcément une modification visible à l'écran (le DOM), mais c'est une étape de calcul qui peut être coûteuse.
Deux situations principales déclenchent le re-rendu d'un composant :
- Un changement de son propre état (via
useState, par exemple). - Le re-rendu de son composant parent.
Ce deuxième point est souvent la source de re-rendus inutiles. Si un composant parent se met à jour, tous ses enfants se re-rendent par défaut, même si leurs propres props n'ont pas changé.
Pour bien visualiser ce processus, la vidéo suivante est un excellent point de départ.
Apprendre React : Mémoisation et useCallback
Cette vidéo de Grafikart.fr explique clairement ce qu'est un rendu, ce qui le déclenche, et pourquoi un composant enfant se re-rend même s'il ne semble pas avoir changé.
Regardez le début de la vidéo, de 00:00:00 à 00:04:08. Concentrez-vous sur les deux raisons qui provoquent un re-rendu d'un composant.
Dans une application R3F, un re-rendu inutile peut signifier recalculer des géométries, réévaluer des logiques complexes dans useFrame, ou déclencher des opérations coûteuses sur le GPU. Il est donc crucial de les maîtriser.
2. Comment Repérer les Re-rendus Inutiles ?
La première étape de toute optimisation est de mesurer. Il ne faut jamais optimiser "à l'aveugle". L'outil indispensable pour cela est le Profiler des Outils de Développement React (React DevTools).
Le Profiler vous permet d'enregistrer les interactions dans votre application et de visualiser quels composants se sont re-rendus, pourquoi, et combien de temps cela a pris.
Apprendre React : Mémoisation et useCallback
La suite de la vidéo précédente montre comment utiliser le Profiler pour identifier les composants qui se re-rendent et qui posent potentiellement problème.
Regardez de 00:04:08 à 00:08:11. Observez comment le graphe en barres (flamegraph) colore les composants qui ont été rendus et comment on peut inspecter la cause du rendu ("rendered because parent rendered").
Une fois que vous avez identifié un composant coûteux qui se re-rend inutilement, vous pouvez utiliser des techniques de mémoïsation.
3. Mémoïser des Calculs Coûteux avec useMemo
La mémoïsation est une technique d'optimisation qui consiste à mettre en cache le résultat d'une fonction et à le retourner si les mêmes entrées se présentent à nouveau, au lieu de recalculer le résultat.
Le hook useMemo applique ce principe. Il est parfait pour des calculs lourds (trier un grand tableau, effectuer une opération mathématique complexe, etc.) qui n'ont pas besoin d'être refaits à chaque rendu si leurs dépendances n'ont pas changé.
const memoizedValue = useMemo(() => {
// Fonction de calcul coûteuse
return computeExpensiveValue(a, b);
}, [a, b]); // Le tableau de dépendances
Le calcul ne sera ré-exécuté que si a ou b change.

La vidéo suivante illustre parfaitement ce concept avec un exemple simple et clair.
Apprendre React : Le hook useMemo
Cette vidéo de Grafikart.fr vous montrera un cas concret où useMemo résout un problème de performance évident.
Regardez les sections de 00:00:00 à 00:03:13. Observez le ralentissement causé par une fonction lente lorsque l'on interagit avec une partie non liée de l'interface, et comment useMemo résout ce problème en ne ré-exécutant le calcul que lorsque c'est nécessaire.
Pour une référence écrite, la documentation officielle est une excellente ressource.
La documentation de React explique en détail le fonctionnement et les cas d'usage de useMemo.
Lisez la section "Sauter des recalculs coûteux". Elle renforce ce que vous venez de voir en vidéo et explique comment déterminer si un calcul est suffisamment coûteux pour justifier la mémoïsation.
4. Mémoïser des Composants avec React.memo
Maintenant que nous savons mémoïser une valeur, comment mémoïser un composant entier ? C'est le rôle de React.memo.
React.memo est un "Composant d'Ordre Supérieur" (Higher-Order Component ou HOC) qui enrobe votre composant. Si le composant enrobé est sur le point de se re-rendre avec les mêmes props que lors du rendu précédent, React sautera le re-rendu et réutilisera le résultat précédent.
function MyComponent(props) {
/* le code du rendu */
}
// Enrobage avec memo
const MemoizedComponent = React.memo(MyComponent);

Reprenons notre première vidéo pour voir React.memo en action.
Apprendre React : Mémoisation et useCallback
La vidéo que nous avons commencée explique comment implémenter React.memo pour empêcher un composant de se re-rendre inutilement.
Regardez la section de 00:09:13 à 00:11:31. Notez comment l'enrobage du composant Info avec memo l'empêche de s'afficher dans les logs de rendu lorsque l'état du parent change.
5. La Synergie Indispensable : memo, useMemo et useCallback
Vous pourriez penser qu'il suffit d'enrober tous vos composants dans React.memo. Malheureusement, c'est un peu plus complexe. React.memo effectue une comparaison superficielle (shallow comparison) des props. Si une des props est un objet, un tableau ou une fonction, cette comparaison échouera la plupart du temps.
Pourquoi ? Parce qu'en JavaScript, les objets, tableaux et fonctions sont des types par référence. Une fonction ou un objet défini à l'intérieur d'un composant est recréé à chaque rendu, obtenant une nouvelle référence en mémoire.
function Parent() {
const [count, setCount] = useState(0);
// `config` est un NOUVEL objet à chaque rendu de Parent
const config = { color: 'blue' };
// `handleClick` est une NOUVELLE fonction à chaque rendu de Parent
const handleClick = () => console.log('clicked');
return (
<div>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
{/* Même si `config` et `handleClick` ont l'air identiques,
leurs références changent, donc MemoizedChild se re-rendra toujours ! */}
<MemoizedChild config={config} onClick={handleClick} />
</div>
);
}
C'est là que useMemo et son cousin, useCallback, deviennent essentiels pour faire fonctionner React.memo :
useMemoest utilisé pour mémoïser des objets ou des tableaux.useCallbackest utilisé pour mémoïser des fonctions.
useCallback est simplement un sucre syntaxique pour useMemo. Les deux lignes suivantes sont équivalentes :useCallback(fn, deps) est équivalent à useMemo(() => fn, deps).
Voyons comment stabiliser nos props pour que React.memo fonctionne enfin.
Apprendre React : Mémoisation et useCallback
La dernière partie de la vidéo de Grafikart explique ce problème des props instables (non-stables) et comment useCallback (présenté ici via useMemo) le résout.
Regardez de 00:11:31 à 00:15:48. C'est la partie la plus importante. Comprenez pourquoi passer une fonction directement en prop annule l'effet de memo, et comment useCallback (ou useMemo pour une fonction) résout ce problème en garantissant que la référence de la fonction reste la même entre les rendus.
La documentation de React explique également très bien cette synergie.
Cette section de la documentation est cruciale. Elle montre comment useMemo est utilisé pour stabiliser un tableau passé en prop à un composant enfant mémoïsé.
Lisez la section "Éviter les rendus superflus de composants". Elle démontre clairement pourquoi un composant List enrobé avec memo continuerait de se re-rendre sans useMemo sur la prop visibleTodos.
6. Règle d'Or : Ne Pas Optimiser Prématurément
La mémoïsation a un coût : elle consomme de la mémoire et ajoute de la complexité. L'utiliser partout serait contre-productif.
La règle d'or est : mesurer d'abord, optimiser ensuite.
- Construisez votre application de la manière la plus simple possible.
- Utilisez le Profiler pour identifier les goulots d'étranglement et les re-rendus lents.
- Appliquez
useMemo,useCallbacketReact.memode manière ciblée, uniquement là où vous avez constaté un réel gain de performance.
Ces articles résument bien cette philosophie.
React Hooks #3 : La performance avec useCallback et useMemo
Cet article fournit d'excellents conseils sur l'utilisation judicieuse des hooks de performance.
Lisez les sections "À noter" et "Conclusion". Elles insistent sur le fait que ces hooks sont des outils d'optimisation, pas de construction, et doivent être utilisés avec parcimonie.
Conclusion
Dans cette leçon, nous avons exploré les fondations de l'optimisation de performance dans React, une compétence indispensable pour le développement 3D avec R3F.
Points clés à retenir :
- Les composants React se re-rendent lorsque leur état change ou lorsque leur parent se re-rend.
- Le Profiler des React DevTools est l'outil principal pour identifier les re-rendus inutiles et coûteux.
useMemomet en cache le résultat d'un calcul pour éviter de le refaire inutilement.React.memomet en cache le rendu d'un composant pour l'éviter si ses props n'ont pas changé.- Pour que
React.memosoit efficace, les props de type objet, tableau ou fonction doivent être stabilisées avecuseMemoouuseCallback. - N'optimisez pas prématurément : mesurez toujours avant d'appliquer des techniques de mémoïsation.
Aperçu de la prochaine leçon :
Maintenant que vous savez optimiser la logique de vos composants, nous allons nous attaquer à un problème spécifique à la 3D : comment afficher des milliers d'objets identiques de manière performante ? La réponse réside dans une technique puissante appelée instancing. Nous verrons comment l'implémenter dans Three.js et R3F avec InstancedMesh.