Cours JavaScript et React · Module 8: Patterns React avancés
Performance Profiling
Dans cette leçon7
Notre voyage cosmique à travers React touche à sa fin, mais avant de partir pour de nouvelles explorations, nous devons nous assurer que notre vaisseau fonctionne à pleine capacité. Dans ce module, nous nous concentrerons sur le profiling des performances des applications React - c'est-à-dire l'identification et l'analyse des « goulots d'étranglement » dans notre code.
Qu'est-ce que le profiling des performances ?
Le profiling des performances est le processus consistant à identifier les endroits d'une application qui consomment le plus de ressources (temps CPU, mémoire) et peuvent entraîner un comportement lent de l'interface utilisateur. C'est comme diagnostiquer les moteurs d'un vaisseau spatial pour vérifier quelles parties ne fonctionnent pas efficacement et peuvent être optimisées.
Dans le contexte des applications React, le profiling te permet de :
- Identifier les composants qui se rendent trop souvent
- Localiser les opérations longues
- Comprendre les patterns de rendu
- Détecter les mises à jour d'état inutiles
- Pointer les zones nécessitant une optimisation
Outils de profiling React
1. React DevTools Profiler
React DevTools est une extension de navigateur qui permet d'analyser les performances des applications React. Elle inclut un onglet « Profiler », qui est l'outil le plus simple et le plus puissant pour profiler les applications React.
Comment utiliser le React DevTools Profiler :
Installer React DevTools
- Pour Chrome : React Developer Tools
- Pour Firefox : React Developer Tools
Démarrer le profiling :
- Ouvre les outils de développement du navigateur
- Va dans l'onglet « Profiler » de React DevTools
- Clique sur le bouton d'enregistrement (cercle rouge)
- Effectue les actions que tu veux profiler dans l'application
- Arrête l'enregistrement
Analyser les résultats :
- Flame Chart - montre la hiérarchie des composants rendus et leur temps de rendu
- Ranked Chart - trie les composants par temps de rendu
- Component Chart - montre quels composants ont été re-rendus à chaque mise à jour
Interpréter les couleurs dans le Profiler :
- Gris - le composant ne s'est pas rendu pendant la mise à jour
- Jaune/Orange/Rouge - le composant s'est rendu, et la couleur indique combien de temps cela a pris (plus c'est rouge, plus c'est long)
- Noir - le composant était la cause de la mise à jour (par exemple, en changeant l'état)
2. Chrome Performance Panel
Chrome DevTools inclut un panneau Performance qui permet un profiling plus général des performances de la page :
Démarrer le profiling :
- Ouvre DevTools dans Chrome (F12)
- Va dans l'onglet « Performance »
- Clique sur le bouton d'enregistrement
- Effectue des actions dans l'application
- Arrête l'enregistrement
Analyser les résultats :
- Main - montre l'activité sur le thread principal (où se déroule la plupart du travail JS)
- Frames - montre les images par seconde (FPS)
- Network - montre l'activité réseau
- Memory - montre l'utilisation mémoire
Ce panneau est particulièrement utile pour identifier :
- Les scripts longs qui bloquent le thread principal
- Les opérations DOM excessives
- Les problèmes de performance de rendu
3. API de profiling intégrée à React
React fournit une API intégrée pour le profiling en mode développement :
1// Importer le composant Profiler
2import { Profiler } from 'react';
3
4// Fonction de callback appelée après le rendu
5function onRenderCallback(
6 id, // identifiant de l'arbre de composants profilé
7 phase, // "mount" (premier rendu) ou "update" (re-render)
8 actualDuration, // temps passé à rendre
9 baseDuration, // temps de rendu estimé de tout le sous-arbre sans optimisations
10 startTime, // quand React a commencé à rendre
11 commitTime, // quand React a fini de rendre
12 interactions // ensemble des « interactions » suivies pour ce rendu
13) {
14 console.log({
15 id,
16 phase,
17 actualDuration,
18 baseDuration,
19 startTime,
20 commitTime
21 });
22}
23
24// Utilisation du composant Profiler dans l'application
25function App() {
26 return (
27 <Profiler id="MissionControl" onRender={onRenderCallback}>
28 <MissionControl />
29 </Profiler>
30 );
31}Tu peux imbriquer des composants Profiler pour profiler différentes parties de l'application :
1function Dashboard() {
2 return (
3 <Profiler id="Dashboard" onRender={onRenderCallback}>
4 <h1>Panneau de contrôle de mission</h1>
5
6 <Profiler id="Navigation" onRender={onRenderCallback}>
7 <Navigation />
8 </Profiler>
9
10 <Profiler id="Stats" onRender={onRenderCallback}>
11 <MissionStats />
12 </Profiler>
13
14 <Profiler id="Crew" onRender={onRenderCallback}>
15 <CrewStatus />
16 </Profiler>
17 </Profiler>
18 );
19}Cette méthode de profiling est particulièrement utile quand :
- Tu veux collecter des données de performance dans un environnement de test
- Tu as des composants spécifiques que tu veux surveiller
- Tu veux automatiser la collecte de données de performance
Problèmes de performance courants et comment les profiler
1. Composants qui se rendent trop fréquemment
Problème : Les composants se re-rendent même quand leurs données n'ont pas changé.
Comment profiler :
- Utilise le React DevTools Profiler pour voir quels composants se rendent trop souvent
- Vérifie si les composants parents provoquent des rendus inutiles des enfants
Exemple du problème :
1function ParentComponent() {
2 const [count, setCount] = useState(0);
3
4 // Cette fonction est recréée à chaque rendu
5 const handleClick = () => {
6 console.log('Button clicked');
7 };
8
9 return (
10 <div>
11 <button onClick={() => setCount(count + 1)}>
12 Augmenter le compteur : {count}
13 </button>
14
15 {/* ChildComponent se re-rendra à chaque changement de count,
16 même si handleClick a la même fonctionnalité */}
17 <ChildComponent onClick={handleClick} />
18 </div>
19 );
20}Solution :
1function ParentComponent() {
2 const [count, setCount] = useState(0);
3
4 // La mémoïsation de la fonction empêche de créer une nouvelle référence à chaque rendu
5 const handleClick = useCallback(() => {
6 console.log('Button clicked');
7 }, []);
8
9 return (
10 <div>
11 <button onClick={() => setCount(count + 1)}>
12 Augmenter le compteur : {count}
13 </button>
14
15 {/* ChildComponent enveloppé dans React.memo ne se re-rendra pas
16 quand ses props ne changent pas */}
17 <MemoizedChildComponent onClick={handleClick} />
18 </div>
19 );
20}
21
22const MemoizedChildComponent = React.memo(ChildComponent);2. Calculs lourds pendant le rendu
Problème : Effectuer des calculs complexes pendant le rendu provoque des ralentissements de l'UI.
Comment profiler :
- Observe dans le React DevTools Profiler quels composants ont le temps de rendu le plus long
- Utilise le Chrome Performance Panel pour voir les longues tâches JavaScript
Exemple du problème :
1function DataAnalytics({ data }) {
2 // Calculs lourds effectués à chaque rendu
3 const processedData = processLargeDataset(data);
4 const statistics = calculateStatistics(processedData);
5
6 return (
7 <div>
8 <h2>Analyse des données de mission</h2>
9 <DataVisualisation stats={statistics} />
10 </div>
11 );
12}Solution :
1function DataAnalytics({ data }) {
2 // Mémoïsation des calculs lourds - ils ne s'exécutent que lorsque data change
3 const processedData = useMemo(() => {
4 return processLargeDataset(data);
5 }, [data]);
6
7 const statistics = useMemo(() => {
8 return calculateStatistics(processedData);
9 }, [processedData]);
10
11 return (
12 <div>
13 <h2>Analyse des données de mission</h2>
14 <DataVisualisation stats={statistics} />
15 </div>
16 );
17}3. Mises à jour d'état excessives
Problème : Des mises à jour d'état trop fréquentes provoquent des re-rendus en cascade.
Comment profiler :
- Dans le React DevTools Profiler, trouve les composants marqués en noir (initiateurs de mise à jour)
- Observe les patterns de mise à jour - certains composants se mettent-ils à jour en groupes ?
Exemple du problème :
1function SpaceshipSensors() {
2 const [temperature, setTemperature] = useState(0);
3 const [pressure, setPressure] = useState(0);
4 const [oxygen, setOxygen] = useState(0);
5 const [radiation, setRadiation] = useState(0);
6
7 // Récupération des données des capteurs toutes les 100ms
8 useEffect(() => {
9 const interval = setInterval(() => {
10 // Chaque mise à jour provoque un rendu distinct
11 setTemperature(getTemperature());
12 setPressure(getPressure());
13 setOxygen(getOxygen());
14 setRadiation(getRadiation());
15 }, 100);
16
17 return () => clearInterval(interval);
18 }, []);
19
20 return (
21 <div>
22 <h2>Relevés des capteurs</h2>
23 <p>Température : {temperature}°C</p>
24 <p>Pression : {pressure} hPa</p>
25 <p>Niveau d'oxygène : {oxygen}%</p>
26 <p>Niveau de radiation : {radiation} μSv/h</p>
27 </div>
28 );
29}Solution :
1function SpaceshipSensors() {
2 // Stocker les données liées ensemble dans un seul objet d'état
3 const [sensorData, setSensorData] = useState({
4 temperature: 0,
5 pressure: 0,
6 oxygen: 0,
7 radiation: 0
8 });
9
10 useEffect(() => {
11 const interval = setInterval(() => {
12 // Une seule mise à jour d'état au lieu de quatre
13 setSensorData({
14 temperature: getTemperature(),
15 pressure: getPressure(),
16 oxygen: getOxygen(),
17 radiation: getRadiation()
18 });
19 }, 100);
20
21 return () => clearInterval(interval);
22 }, []);
23
24 return (
25 <div>
26 <h2>Relevés des capteurs</h2>
27 <p>Température : {sensorData.temperature}°C</p>
28 <p>Pression : {sensorData.pressure} hPa</p>
29 <p>Niveau d'oxygène : {sensorData.oxygen}%</p>
30 <p>Niveau de radiation : {sensorData.radiation} μSv/h</p>
31 </div>
32 );
33}4. Grandes listes et visualisations de données
Problème : Le rendu de longues listes ou de visualisations complexes peut ralentir l'interface.
Comment profiler :
- Utilise le React DevTools Profiler pour identifier les composants de liste qui mettent longtemps à se rendre
- Vérifie à quelle fréquence les éléments individuels de la liste se rendent
Exemple d'utilisation de React DevTools pour profiler les performances d'une liste :
- Démarre le profiling
- Fais défiler la liste pour voir comment l'application répond
- Arrête le profiling
- Vérifie quels composants ont le temps de rendu le plus long dans le ranked chart
Solution :
- Virtualisation de liste (React Window, React Virtualized)
- Mémoïsation des éléments de liste
- Pagination au lieu d'afficher tous les éléments d'un coup
Techniques de profiling avancées
1. User Timing API
L'API Performance du navigateur permet de créer des marqueurs de temps personnalisés :
1function ExpensiveComponent({ data }) {
2 useEffect(() => {
3 // Début de la mesure
4 performance.mark('expensive-calculation-start');
5
6 // Effectuer des calculs coûteux
7 const result = performExpensiveCalculation(data);
8
9 // Fin de la mesure
10 performance.mark('expensive-calculation-end');
11
12 // Créer une mesure entre les marqueurs
13 performance.measure(
14 'expensive-calculation',
15 'expensive-calculation-start',
16 'expensive-calculation-end'
17 );
18
19 // Afficher les résultats
20 console.log(performance.getEntriesByName('expensive-calculation'));
21 }, [data]);
22
23 //
24}Ces marqueurs seront visibles dans le panneau Performance de Chrome DevTools.
2. Surveillance des performances en production
Pour surveiller les performances en production, tu peux utiliser :
- Web Vitals - une bibliothèque Google pour mesurer les indicateurs de performance clés
- New Relic, Datadog, Sentry - outils APM (Application Performance Monitoring)
- Des solutions personnalisées utilisant l'API Performance et l'API Profiler
Exemple de collecte de données depuis le React Profiler et envoi vers un serveur :
1function onRenderCallback(id, phase, actualDuration, baseDuration, startTime, commitTime) {
2 // Nous ne collectons que les données de la phase update qui prend plus de 10ms
3 if (phase === 'update' && actualDuration > 10) {
4 sendToAnalyticsServer({
5 componentId: id,
6 renderTime: actualDuration,
7 timestamp: Date.now()
8 });
9 }
10}
11
12function App() {
13 return (
14 <Profiler id="App" onRender={onRenderCallback}>
15 <AppContent />
16 </Profiler>
17 );
18}Étude de cas : profiling et optimisation d'une application React
Analysons le processus de profiling et d'optimisation des performances d'une application React à travers l'exemple d'un panneau de contrôle de mission spatiale :
Étape 1 : Identifier le problème
Les utilisateurs signalent que le panneau de contrôle est lent, surtout lors de l'affichage des données de télémétrie et de la liste des événements.
Étape 2 : Profiler l'application
Nous utilisons le React DevTools Profiler pour identifier la source du problème :
- Nous enregistrons une session pendant une utilisation normale du panneau
- Nous analysons les résultats dans le Profiler
- Nous découvrons que :
- Le composant
TelemetryChartse rend très lentement (>100ms) - Le composant
EventsListse re-rend chaque fois que de nouvelles données de télémétrie arrivent - La fonction
calculateTrendsconsomme beaucoup de temps CPU
- Le composant
Étape 3 : Optimisation
Sur la base des résultats du profiling, nous introduisons les optimisations suivantes :
- Optimiser TelemetryChart :
1// Avant optimisation
2function TelemetryChart({ data }) {
3 // Traitement lourd des données à chaque rendu
4 const chartData = processChartData(data);
5
6 return <LineChart data={chartData} />;
7}
8
9// Après optimisation
10const TelemetryChart = React.memo(function TelemetryChart({ data }) {
11 // Traitement des données mémoïsé
12 const chartData = useMemo(() => processChartData(data), [data]);
13
14 return <LineChart data={chartData} />;
15});- Optimiser EventsList :
1// Avant optimisation
2function EventsList({ events }) {
3 return (
4 <div>
5 {events.map(event => (
6 <EventItem key={event.id} event={event} />
7 ))}
8 </div>
9 );
10}
11
12// Après optimisation
13const EventsList = React.memo(function EventsList({ events }) {
14 return (
15 <div>
16 {events.map(event => (
17 <MemoizedEventItem key={event.id} event={event} />
18 ))}
19 </div>
20 );
21});
22
23const MemoizedEventItem = React.memo(EventItem);- Déplacer les calculs lourds vers un web worker :
1// Avant optimisation
2function MissionControlPanel({ telemetryData }) {
3 const trends = calculateTrends(telemetryData); // Bloque le thread principal
4
5 return (
6 <div>
7 <TrendsDisplay trends={trends} />
8 {/* autres composants */}
9 </div>
10 );
11}
12
13// Après optimisation
14function MissionControlPanel({ telemetryData }) {
15 const [trends, setTrends] = useState(null);
16
17 useEffect(() => {
18 if (!telemetryData) return;
19
20 // Créer un web worker
21 const worker = new Worker('./trendsWorker.js');
22
23 // Écouter la réponse
24 worker.addEventListener('message', (event) => {
25 setTrends(event.data);
26 worker.terminate();
27 });
28
29 // Envoyer les données pour traitement
30 worker.postMessage(telemetryData);
31 }, [telemetryData]);
32
33 return (
34 <div>
35 {trends ? <TrendsDisplay trends={trends} /> : <LoadingSpinner />}
36 {/* autres composants */}
37 </div>
38 );
39}Étape 4 : Vérifier les optimisations
Après avoir introduit les optimisations, nous profilons à nouveau l'application :
- Le temps de rendu de
TelemetryChartest passé de >100ms à ~15ms - Le composant
EventsListne se rend plus lors des mises à jour des données de télémétrie - Le thread principal n'est plus bloqué par
calculateTrends
Grâce à ces optimisations, l'interface utilisateur fonctionne de façon fluide, et le panneau de contrôle répond immédiatement aux interactions de l'utilisateur.
Bonnes pratiques de profiling
Profile dans un environnement proche de la production
- Lance l'application en mode production (
npm run buildet sers les fichiers construits) - Teste sur des appareils similaires à ceux utilisés par les utilisateurs
- Lance l'application en mode production (
Établis des références (baselines)
- Mesure les performances avant et après les optimisations
- Fixe des objectifs de performance précis (par exemple, temps du premier rendu < 2s)
Profile régulièrement
- Intègre le profiling dans le cycle de développement de l'application
- Surveille les performances après l'introduction de changements majeurs
Concentre-toi sur la perception de l'utilisateur
- Priorise les optimisations qui ont le plus d'impact sur la performance perçue
- Souviens-toi des métriques clés Web Vitals (LCP, FID, CLS)
Documente les résultats du profiling
- Enregistre les données de performance avant et après les optimisations
- Partage les connaissances et techniques d'optimisation avec l'équipe
Résumé
Le profiling des performances est un élément essentiel de la création d'applications React de haute qualité. Tout comme les ingénieurs spatiaux surveillent chaque aspect du fonctionnement d'un vaisseau, nous devrions analyser en continu les performances de nos applications pour garantir aux utilisateurs une expérience fluide et réactive.
Points clés à retenir :
- Utilise les bons outils - React DevTools Profiler, Chrome Performance Panel
- Identifie les goulots d'étranglement - trouve les composants qui se rendent trop souvent ou trop longtemps
- Applique les techniques d'optimisation appropriées - mémoïsation, éviter les rendus inutiles, virtualisation de listes
- Mesure l'efficacité des optimisations - compare les performances avant et après les changements
- Optimise avec modération - souviens-toi qu'une optimisation prématurée peut conduire à du code trop compliqué
Avec ces techniques et outils de profiling, tu seras capable de créer des applications React performantes qui offrent une excellente expérience aux utilisateurs, même dans les interfaces de panneau de contrôle cosmique les plus complexes.
Code de cette leçon : App.jsx
1import React, { useState, useMemo, useCallback, useEffect, useRef, Profiler } from 'react';
2
3// ============================================
4// PROFILING DES PERFORMANCES REACT
5// ============================================
6// Cet exemple démontre des techniques d'optimisation
7// et de profiling des composants React.
8
9// --- Callback de profiling ---
10function onRenderCallback(id, phase, actualDuration, baseDuration) {
11 console.log(
12 `[Profiler] ${id} | phase: ${phase} | actual: ${actualDuration.toFixed(2)}ms | base: ${baseDuration.toFixed(2)}ms`
13 );
14}
15
16// --- Simulation d'un calcul lourd ---
17function expensiveCalculation(items) {
18 let result = 0;
19 for (let i = 0; i < items.length; i++) {
20 for (let j = 0; j < 1000; j++) {
21 result += Math.sqrt(items[i].value * j + 1);
22 }
23 }
24 return result.toFixed(2);
25}
26
27// --- Composant SANS optimisation ---
28function UnoptimizedChild({ label, onClick }) {
29 const renderCount = useRef(0);
30 renderCount.current += 1;
31
32 return (
33 <div style={childStyle}>
34 <span>{label}</span>
35 <span style={renderBadge}>{renderCount.current}x rendu</span>
36 <button onClick={onClick} style={btnStyle}>Cliquer</button>
37 </div>
38 );
39}
40
41// --- Composant AVEC optimisation (React.memo) ---
42const OptimizedChild = React.memo(function OptimizedChild({ label, onClick }) {
43 const renderCount = useRef(0);
44 renderCount.current += 1;
45
46 return (
47 <div style={{ ...childStyle, borderColor: '#4CAF50' }}>
48 <span>{label}</span>
49 <span style={{ ...renderBadge, background: '#4CAF50' }}>{renderCount.current}x rendu</span>
50 <button onClick={onClick} style={{ ...btnStyle, background: '#4CAF50' }}>Cliquer</button>
51 </div>
52 );
53});
54
55// --- Composant de liste avec virtualisation (simplifiee) ---
56function VirtualizedList({ items, itemHeight = 40, containerHeight = 200 }) {
57 const [scrollTop, setScrollTop] = useState(0);
58 const containerRef = useRef(null);
59
60 const visibleStart = Math.floor(scrollTop / itemHeight);
61 const visibleCount = Math.ceil(containerHeight / itemHeight) + 1;
62 const visibleItems = items.slice(visibleStart, visibleStart + visibleCount);
63
64 return (
65 <div
66 ref={containerRef}
67 onScroll={(e) => setScrollTop(e.target.scrollTop)}
68 style={{
69 height: containerHeight, overflow: 'auto',
70 background: '#0d1b2a', borderRadius: '8px', border: '1px solid #2a3f5f',
71 }}
72 >
73 <div style={{ height: items.length * itemHeight, position: 'relative' }}>
74 {visibleItems.map((item, i) => (
75 <div
76 key={item.id}
77 style={{
78 position: 'absolute', top: (visibleStart + i) * itemHeight,
79 height: itemHeight, width: '100%', padding: '8px 12px',
80 boxSizing: 'border-box', borderBottom: '1px solid #1b2838',
81 color: '#e0e1dd', display: 'flex', alignItems: 'center',
82 }}
83 >
84 {item.name} - valeur : {item.value}
85 </div>
86 ))}
87 </div>
88 </div>
89 );
90}
91
92// --- Application principale ---
93function App() {
94 const [count, setCount] = useState(0);
95 const [filterText, setFilterText] = useState('');
96 const [showOptimized, setShowOptimized] = useState(false);
97
98 // Nous generons une liste de 500 elements
99 const largeList = useMemo(() => {
100 return Array.from({ length: 500 }, (_, i) => ({
101 id: i,
102 name: `Mission #${i + 1}`,
103 value: Math.floor(Math.random() * 1000),
104 }));
105 }, []);
106
107 // Filtrage avec useMemo - les calculs ne s'executent que lorsque les donnees changent
108 const filteredItems = useMemo(() => {
109 return largeList.filter((item) =>
110 item.name.toLowerCase().includes(filterText.toLowerCase())
111 );
112 }, [largeList, filterText]);
113
114 // Calcul lourd avec memoisation
115 const expensiveResult = useMemo(() => {
116 return expensiveCalculation(filteredItems.slice(0, 50));
117 }, [filteredItems]);
118
119 // Callback SANS memoisation (nouvelle reference a chaque rendu)
120 const handleClickUnoptimized = () => {
121 console.log('Clic sans mémoïsation');
122 };
123
124 // Callback AVEC memoisation (reference stable)
125 const handleClickOptimized = useCallback(() => {
126 console.log('Clic avec useCallback');
127 }, []);
128
129 return (
130 <div style={appStyle}>
131 <h1 style={{ color: '#ffd700', textAlign: 'center', marginBottom: '8px' }}>
132 Profiling des performances React
133 </h1>
134 <p style={{ textAlign: 'center', color: '#aaa', marginBottom: '20px' }}>
135 Ouvre la console du navigateur pour voir les logs du Profiler
136 </p>
137
138 {/* Section 1 : React.memo + useCallback */}
139 <Profiler id="MemoDemo" onRender={onRenderCallback}>
140 <div style={sectionStyle}>
141 <h2 style={headingStyle}>React.memo + useCallback</h2>
142 <p style={{ color: '#aaa', marginBottom: '12px' }}>
143 Clique sur "+1" et observe quel composant se re-rend.
144 </p>
145 <div style={{ textAlign: 'center', marginBottom: '16px' }}>
146 <span style={{ fontSize: '24px', marginRight: '12px' }}>Counter: {count}</span>
147 <button onClick={() => setCount((c) => c + 1)} style={btnAccent}>+1</button>
148 </div>
149
150 <div style={{ display: 'flex', gap: '12px', flexWrap: 'wrap' }}>
151 <div style={{ flex: 1, minWidth: '220px' }}>
152 <h3 style={{ color: '#ff6b6b' }}>Sans optimisation</h3>
153 <UnoptimizedChild
154 label="Composant A"
155 onClick={handleClickUnoptimized}
156 />
157 </div>
158 <div style={{ flex: 1, minWidth: '220px' }}>
159 <h3 style={{ color: '#4CAF50' }}>Avec React.memo + useCallback</h3>
160 <OptimizedChild
161 label="Composant B"
162 onClick={handleClickOptimized}
163 />
164 </div>
165 </div>
166 </div>
167 </Profiler>
168
169 {/* Section 2 : useMemo sur des calculs lourds */}
170 <Profiler id="UseMemoDemo" onRender={onRenderCallback}>
171 <div style={sectionStyle}>
172 <h2 style={headingStyle}>useMemo - calculs lourds</h2>
173 <p style={{ color: '#aaa', marginBottom: '12px' }}>
174 Le filtrage et les calculs sont mémoïsés - changer le filtre recalcule le résultat.
175 </p>
176 <input
177 value={filterText}
178 onChange={(e) => setFilterText(e.target.value)}
179 placeholder="Filtrer les missions..."
180 style={inputStyle}
181 />
182 <p style={{ margin: '8px 0', color: '#ffd700' }}>
183 Résultat du calcul : {expensiveResult} | Trouvé : {filteredItems.length} missions
184 </p>
185 </div>
186 </Profiler>
187
188 {/* Section 3 : Virtualisation de la liste */}
189 <Profiler id="VirtualizedListDemo" onRender={onRenderCallback}>
190 <div style={sectionStyle}>
191 <h2 style={headingStyle}>Virtualisation de la liste (500 éléments)</h2>
192 <p style={{ color: '#aaa', marginBottom: '12px' }}>
193 Seuls les éléments visibles sont rendus - fais défiler la liste.
194 </p>
195 <label style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '12px', cursor: 'pointer' }}>
196 <input
197 type="checkbox"
198 checked={showOptimized}
199 onChange={() => setShowOptimized(!showOptimized)}
200 />
201 Afficher la liste virtualisée
202 </label>
203
204 {showOptimized ? (
205 <VirtualizedList items={filteredItems} />
206 ) : (
207 <div style={{
208 height: 200, overflow: 'auto', background: '#0d1b2a',
209 borderRadius: '8px', border: '1px solid #2a3f5f',
210 }}>
211 {filteredItems.map((item) => (
212 <div key={item.id} style={{
213 padding: '8px 12px', borderBottom: '1px solid #1b2838',
214 color: '#e0e1dd',
215 }}>
216 {item.name} - valeur : {item.value}
217 </div>
218 ))}
219 </div>
220 )}
221 </div>
222 </Profiler>
223
224 {/* TODO : Ajoute une section comparant les performances
225 de deux approches de mise à jour de l'état :
226 1. Plusieurs useState séparés (température, pression, oxygène)
227 2. Un seul useState avec un objet { temperature, pressure, oxygen }
228 Utilise le composant Profiler pour mesurer les différences. */}
229 </div>
230 );
231}
232
233// --- Styles ---
234const appStyle = {
235 background: '#0d1b2a', minHeight: '100vh', padding: '20px',
236 fontFamily: 'Arial, sans-serif', color: '#e0e1dd',
237};
238const sectionStyle = {
239 background: '#1b2838', borderRadius: '12px', padding: '20px',
240 maxWidth: '700px', margin: '16px auto', border: '1px solid #2a3f5f',
241};
242const headingStyle = { color: '#00d4ff', marginBottom: '8px' };
243const childStyle = {
244 display: 'flex', alignItems: 'center', gap: '10px',
245 padding: '10px', background: '#0d1b2a', borderRadius: '8px',
246 border: '1px solid #ff6b6b', marginBottom: '8px',
247};
248const renderBadge = {
249 background: '#ff6b6b', color: '#fff', padding: '2px 8px',
250 borderRadius: '12px', fontSize: '12px',
251};
252const btnStyle = {
253 marginLeft: 'auto', padding: '6px 14px', background: '#ff6b6b',
254 color: '#fff', border: 'none', borderRadius: '6px', cursor: 'pointer',
255};
256const btnAccent = {
257 padding: '8px 20px', background: '#ffd700', color: '#000',
258 border: 'none', borderRadius: '8px', cursor: 'pointer', fontWeight: 'bold',
259};
260const inputStyle = {
261 width: '100%', padding: '8px 12px', background: '#0d1b2a',
262 border: '1px solid #2a3f5f', borderRadius: '6px', color: '#e0e1dd',
263 fontSize: '14px', boxSizing: 'border-box',
264};
265
266export default App;Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Que fait React.memo() et quand faut-il l'utiliser ?
2. À quoi sert le React Profiler dans les React DevTools ?
Tâches pratiques dans le jeu
- Éditeur de code
Chargement dynamique de modules
- Tri horizontal
Ordonne la syntaxe de Suspense avec une UI de repli (fallback) :
- Tri vertical
Ordonne les stratégies de test par priorité :
- Éditeur de code
Custom hook useForm avec validation