Avancé55 min45 XP
Objectifs de cette leçon
- Optimiser les rendus React Native
- Utiliser les hooks de performance
- Profiler et résoudre les goulots d'étranglement
Performance en React Native ⚡
Une app lente = des utilisateurs mécontents. React Native offre plusieurs techniques pour garder ton app fluide, même avec des listes de 10 000 éléments !
1. Éviter les re-renders inutiles 🔄
' À chaque fois que l'état change, React re-rend le composant ' Problème : si le parent re-rend, TOUS les enfants re-rendent aussi ! ' Solution : dire à React "ne re-rend que si les props changent" ``' tsx import React from "react"; import { View, Text } from "react-native"; // React.memo = le composant ne re-rend que si SES PROPS changent interface TexteProps { label: string; valeur: string | number; } const TexteInfo = React.memo(function TexteInfo({ label, valeur }: TexteProps) { console.log(`🔄 Re-rend : ${label}`); return ( <Text> {label} : {valeur} </Text> ); }); // Parent function Dashboard({ utilisateur, stats }: { utilisateur: any; stats: any }) { return ( <View> {/* TexteInfo ne re-rendra PAS si utilisateur.nom et stats.total n'ont pas changé */} <TexteInfo label="Nom" valeur={utilisateur.nom} /> <TexteInfo label="Total" valeur={stats.total} /> </View> ); } ``' --- ## 2. useMemo et useCallback — mémoïsation 🧠 ```text ' useMemo = éviter de RECALCULER une valeur à chaque rendu ' useCallback = éviter de RECRÉER une fonction à chaque rendu ' Sans useMemo : ' const trie = items.sort(...) → recalculé à CHAQUE rendu ! ' ' Avec useMemo : ' const trie = useMemo(() => items.sort(...), [items]) ' → recalculé SEULEMENT si items change
import { useMemo, useCallback } from "react"; function ListeArticles({ articles }: { articles: Article[] }) { // useMemo : ne trier que si la liste change const articlesTries = useMemo(() => { console.log("🔄 Tri en cours..."); return [...articles].sort((a, b) => b.note - a.note); }, [articles]); // useCallback : garder la même fonction const handlePress = useCallback((id: number) => { console.log("Article cliqué :", id); }, []); // [] = jamais recréée return ( <FlatList data={articlesTries} renderItem={({ item }) => ( <ArticleCard article={item} onPress={handlePress} /> )} /> ); } ``' --- ## 3. Optimisation des images 🖼️ ```text ' Les images sont la première cause de lenteur ! ' Solutions : ' 1. Redimensionner les images AVANT de les charger ' 2. Utiliser des formats modernes (WebP) ' 3. Mettre en cache les images téléchargées
import { Image } from "expo-image"; // ✅ Meilleur que Image classique // Image = chargement optimisé + cache + placeholder <Image source={{ uri: "https://..." }} style={{ width: 200, height: 200 }} placeholder={{ blurhash: "L6PZfSi_.AyE_3t7t7R**0o#DgR4" }} // Placeholder contentFit="cover" transition={300} // Animation de fade /> // Redimensionnement côté serveur (ex: avec Cloudinary) // https://res.cloudinary.com/.../w_200,h_200/image.jpg // → L'image est déjà redimensionnée AVANT d'être téléchargée ! ``' --- ## 4. Comprendre le bridge JS et JSI/Fabric 🔧 text ' Ancienne architecture (jusqu'à RN 0.67) : ' Le Bridge JSON = un tube qui ralentit tout ' Chaque communication JS <-> Native doit être "sérialisée" en JSON ' → Lent, surtout pour les animations ' Nouvelle architecture (RN 0.68+) : ' JSI (JavaScript Interface) = communication DIRECTE ' Plus besoin de sérialiser en JSON ' → 2x plus rapide ! ' Fabric (nouveau renderer) + TurboModules ' → Animations à 60 FPS au lieu de 30 ! ``' --- ## 5. Profiling — trouver les lenteurs 🔍 ```bash # Activer le profiling React Native # 1. Ouvrir le menu développeur (Cmd/Ctrl + D) # 2. "Show Perf Monitor" → FPS en temps réel # 3. "Profile Hermes" → profiling détaillé # React DevTools npx react-devtools ``' ```tsx import { Profiler } from "react"; function onRenderCallback( id: string, phase: "mount" | "update", actualDuration: number, ) { console.log(`${id} (${phase}) : ${actualDuration.toFixed(2)}ms`); // Si > 16ms → l'app va ralentir (60 FPS = 16ms par frame) } function App() { return ( <Profiler id="Liste" onRender={onRenderCallback}> <MaListe items={items} /> </Profiler> ); } ``' --- ## 6. Checklist performance ✅ | Problème | Symptôme | Solution | |----------|----------|----------| | **Listes lentes** | Défilement saccadé | FlatList + getItemLayout | | **Images lourdes** | Chargement lent | Redimensionner + cache | | **Re-renders** | Lag aux interactions | React.memo + useMemo | | **Animations** | Saccades | Reanimated + useNativeDriver | | **Bridge** | Tout est lent | Nouvelle archi (JSI/Fabric) | --- ## Exercices pour toi 🎯 1. **React.memo** : crée une liste de 1000 éléments avec et sans React.memo, mesure la différence de FPS 2. **FlatList optimisation** : ajoute `getItemLayout`, `windowSize` et `maxToRenderPerBatch` à ta FlatList et observe 3. **Image** : crée une galerie avec des images 2000×2000 vs images redimensionnées 200×200 — compare le temps de chargement
Création des variables pas à pas
Prêt à explorer la mémoire
Appuie sur Play pour voir les variables se créer une par une
0/4
Visualisation d'exécution
ligne 17code
1import React, { useState, useCallback } from 'react';
2import { View, Text, TouchableOpacity, FlatList } from 'react-native';
3
4// Composant optimisé avec memo
5const Article = React.memo(({ titre, prix }: { titre: string; prix: number }) => (
6 <View>
7 <Text>{titre} - {prix}€</Text>
8 </View>
9));
10
11export default function Liste() {
12 const [compte, setCompte] = useState(0);
13 const articles = [
14 { id: '1', titre: 'Livre', prix: 15 },
15 { id: '2', titre: 'Stylo', prix: 3 },
16 ];
17
18 return (
19 <View>
20 <TouchableOpacity onPress={() => setCompte(c => c + 1)}>
21 <Text>Clics: {compte}</Text>
22 </TouchableOpacity>
23 <FlatList
24 data={articles}
25 renderItem={({ item }) => <Article titre={item.titre} prix={item.prix} />}
26 />
27 </View>
28 );
29}
Variables
Article (memo)=ne re-rend pas si props identiques
FlatList=virt. rend 2/10 000 items
⚡ React.memo — Avant / Après
❌ SANS React.memo
Enfant 0 renders
Re-rend à CHAQUE clic parent
✅ AVEC React.memo
Enfant 0 renders
Ne re-rend QUE si ses props changent