React Native combine JavaScript et les environnements d'exécution natifs, ce qui rend le débogage puissant mais parfois délicat. La voie la plus rapide pour sortir de la plupart des échecs est un flux de travail cohérent et reproductible en six étapes : inventorier votre environnement et vos versions, reproduire avec une portée réduite, capturer les bons journaux, appliquer le plus petit changement sûr, vérifier, et annuler proprement si nécessaire. Ce guide fournit des commandes pratiques à copier-coller et des pointeurs de configuration pour les flux de travail bare et Expo sur Android et iOS. Vous apprendrez à lire les bons journaux, valider le comportement réseau, et corriger les problèmes courants de résolution de modules, de builds natifs, d'appels REST, de Google Play Billing, Stripe et RevenueCat. Chaque exemple inclut les résultats attendus, les modes d'échec et les étapes de récupération fiables au quotidien. ## TL;DR — 5 commandes qui résolvent 80 % des incidents - npx react-native info — capturer l'inventaire de référence - npx react-native start --reset-cache — vider le cache Metro - cd android && ./gradlew clean assembleDebug --stacktrace — build Android propre - cd ios && pod deintegrate && pod install && cd .. — réinitialiser les pods iOS - rm -rf node_modules && npm ci — rafraîchissement universel des dépendances ## Prérequis et hypothèses Ce guide couvre React Native 0.72–0.76 sur trois types de flux de travail : bare workflow, Expo managed workflow et Expo development client. Systèmes d'exploitation hôtes : macOS (requis pour iOS), Linux et Windows. Ligne d'outils de base requise : Node 18 ou 20 LTS ; gestionnaire de paquets unique (npm 9+, Yarn 1.x ou pnpm 8+) ; JDK 17 ou 21 aligné avec Android Gradle Plugin (AGP) 8.x ; Android SDK avec platform-tools et build-tools 34+ ; Xcode 15+ avec cible de déploiement iOS 15.1+ ; CocoaPods 1.13+ ; Ruby 3.2+ (via rbenv/rvm/chruby, pas Ruby système) ; Watchman 2024+ ; fastlane récent si utilisé pour CI/CD. Non couverts : React Native <0.70, bases de code architecture classique uniquement sans Hermes, installations globales legacy react-native-cli, ou builds natifs Windows (React Native for Windows hors périmètre). ## Architecture et flux des journaux (texte) [Metro Bundler] --(bundle JS)--> [Hermes/JSC Runtime] --(JSI)--> [Native Bridge] - +-- Android: logcat (ReactNative, ReactNativeJS, System.err) - +-- iOS: OSLog / Console Xcode (sous-système: com.apple.reactnative) - +-- Crashs bytecode Hermes -> /data/data//files/.hermes/*.log - +-- Journaux Metro (stdout/stderr, événements HMR, graphe de résolution) Nouvelle architecture (Fabric/TurboModules) : [Codegen] -> [C++/ObjC/Swift/Kotlin généré] -> [Fabric Renderer / TurboModule Registry] Chemins des journaux : Metro (sortie Codegen), Gradle (generateCodegenArtifactsFromSchema), Xcode (journaux montage Fabric), logcat (enregistrement TurboModule) ## Inventaire de l'environnement (développé) Exécutez ce qui suit à la racine du projet pour remplir un en-tête d'incident. Copiez le modèle ci-dessous dans votre ticket. # Modèle d'en-tête d'incident — copier la sortie dans le ticket echo "=== EN-TÊTE INCIDENT ===" echo "Date: $(date -u +"%Y-%m-%dT%H:%M:%SZ")" echo "Commit Git: $(git rev-parse HEAD 2>/dev/null || echo 'N/A')" echo "Branche: $(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'N/A')" npx react-native info echo "--- Expo doctor ---" npx expo doctor 2>/dev/null || echo "Expo CLI non disponible" echo "--- Community CLI doctor ---" npx @react-native-community/cli doctor 2>/dev/null || echo "Community CLI non disponible" echo "--- Gradle ---" gradle -v 2>/dev/null || (cd android && ./gradlew -v 2>/dev/null) || echo "Gradle non trouvé" echo "--- Xcode SDKs ---" xcodebuild -showsdks 2>/dev/null | head -20 echo "--- Ruby ---" ruby -v && which ruby echo "--- Watchman ---" watchman --version 2>/dev/null || echo "Watchman non installé" echo "--- Fastlane ---" fastlane --version 2>/dev/null || echo "Fastlane non installé" echo "--- Node/PM ---" node -v && (npm -v || yarn -v || pnpm -v) echo "=== FIN EN-TÊTE ===" Éléments clés d'inventaire à enregistrer : version React Native et type de template ; moteur JS (Hermes par défaut, JSC en option) ; versions Node et gestionnaire de paquets avec type de lockfile ; distribution et version JDK, version AGP depuis android/build.gradle ou gradle/libs.versions.toml ; compileSdk et targetSdk Android SDK ; version Xcode, version CocoaPods, gestionnaire Ruby ; cible de déploiement iOS depuis Podfile ; version Watchman ; version fastlane si applicable. ## Configuration de base sécurisée (développée) Adoptez ces valeurs par défaut avant le travail d'incident pour réduire le rayon d'impact. Gestion des paquets : Utilisez exactement un gestionnaire de paquets. Committez package-lock.json (npm), yarn.lock (Yarn 1.x) ou pnpm-lock.yaml (pnpm). Évitez les versions flottantes ( ^ , ~ ) dans package.json pendant les incidents ; épinglez les versions exactes. Résolution Metro ( metro.config.js ) : module.exports = { resolver: { unstable_enablePackageExports: true, sourceExts: ['js', 'jsx', 'json', 'ts', 'tsx', 'mjs'], // Ajouter résolution personnalisée pour monorepos si nécessaire }, }; Configuration React Native ( react-native.config.js ) : module.exports = { assets: ['./assets/fonts'], project: { ios: {}, android: {}, }, }; Propriétés Gradle ( android/gradle.properties ) : org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m org.gradle.parallel=true org.gradle.caching=true android.enableJetifier=true # Hermes par défaut hermes_enabled=true Wrapper Gradle ( android/gradle/wrapper/gradle-wrapper.properties ) : distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip Podfile ( ios/Podfile ) : platform :ios, '15.1' use_frameworks! :linkage => :static # ou :dynamic pour pods Swift $RNFirebaseAsStaticFramework = true # si utilisation Firebase Secrets et configuration : N'intégrez jamais de secrets (clés secrètes Stripe, jetons API) dans le client. Utilisez react-native-config (bare) ou expo-constants avec extra (Expo) pour les URLs de base et clés publiables selon l'environnement. Gardez les drapeaux debug-only (cleartext, exceptions ATS) hors des builds de release via variantes de build/xcconfigs. ## Commandes de réinitialisation de base (matrice plateforme)
Flux de travailAndroidiOSNotes
Barecd android && ./gradlew clean assembleDebug --stacktracecd ios && pod deintegrate && pod install --repo-update && cd ..Exécuter adb reverse tcp:8081 tcp:8081 pour débogage appareil
Expo Managednpx expo prebuild --clean --platform android puis cd android && ./gradlew clean assembleDebugnpx expo prebuild --clean --platform ios puis cd ios && pod install --repo-updatenpx expo start -c vide le cache Metro
Expo Dev Clienteas build --profile development --platform android --localeas build --profile development --platform ios --localJournaux client dev via adb logcat / Console Xcode
Tousnpx react-native start --reset-cachenpx react-native start --reset-cacheRéinitialisation cache Metro universelle
Réinitialisation simulateuradb emu kill + cold bootxcrun simctl erase allOption nucléaire pour état obsolète
Équivalents Windows PowerShell : Set-Content -Path android\local.properties -Value "sdk.dir=$env:ANDROID_SDK_ROOT" .\android\gradlew.bat clean assembleDebug --stacktrace npx expo prebuild --clean --platform android ## Sources de journaux et corrélation Cible Source Commande / Accès Metro bundler Terminal stdout/stderr npx react-native start --reset-cache Journaux appareil Android logcat (filtré) adb logcat -s ReactNative:V ReactNativeJS:V System.err:V -v threadtime Build Android Gradle cd android && ./gradlew assembleDebug --stacktrace --scan Runtime iOS Console Xcode / OSLog xcrun simctl spawn booted log stream --predicate 'process == "MyApp"' --style compact Vidages crash Hermes Système de fichiers appareil adb shell run-as com.myapp cat files/.hermes/*.log | npx metro-symbolicate Expo managed Expo CLI / Metro npx expo start -c EAS Build Tableau de bord EAS / CLI eas build:list --platform=android --limit=5 puis eas build:view expo-updates Journaux appareil adb logcat -s ExpoUpdates:V / Console Xcode filtre ExpoUpdates Codegen Nouvelle Architecture Gradle / Xcode ./gradlew generateCodegenArtifactsFromSchema --stacktrace / Journal build Xcode Codegen Analyse de crash Hermes : Après un crash natif sur Android, récupérez le journal Hermes et symbolisez : adb shell run-as com.myapp cat files/.hermes/*.log | npx metro-symbolicate > hermes-crash.txt # Pour profilage, activer profileur Hermes : adb shell setprop debug.hermes.profiler 1 # Puis ouvrir chrome://tracing et charger le profil généré ## Modèles de dépannage (nouveaux) ### Dérive Codegen / Configuration TypeScript Symptôme : error: cannot find module 'NativeMyModule' ou Codegen échoue avec erreurs TypeScript. Cause : react-native-codegen attend une configuration TypeScript stricte ; incompatibilités js/ts dans les fichiers spec. Correctif : # Vérifier que Codegen peut analyser vos specs npx react-native-codegen --help # En bare workflow, régénérer cd android && ./gradlew generateCodegenArtifactsFromSchema --stacktrace cd ../ios && pod install --repo-update Attendu : Fichiers C++/Swift/Kotlin générés sous build/generated/codegen (Android) et Pods/Headers/Private/React-Core/ReactCommon (iOS). Échec : TypeScript strict: false ou skipLibCheck manquant dans tsconfig.json . Récupération : Aligner tsconfig.json sur les défauts du template RN ; assurer que la version react-native-codegen correspond à la version RN. ### TurboModule « Native module cannot be null » Symptôme : Erreur JS Native module cannot be null pour un TurboModule personnalisé. Cause : Module non enregistré dans ReactNativeHost (Android) ou RCTBridge (iOS), ou Package non ajouté. Correctif : Vérifier que MainApplication.java / MainApplication.kt inclut new MyTurboModulePackage() dans getPackages() . Sur iOS, s'assurer que RCTAppSetupDefaultRootView inclut le module ou que l'autolinking l'a détecté ( pod install --repo-update ). Vérification : adb logcat -s ReactNative:V montre le succès de TurboModuleRegistry.getEnforcing . ### Fabric « ShadowNode not found » Symptôme : Crash dans FabricUIManager avec ShadowNode not found for tag . Cause : Incompatibilité de version entre react-native et react-native-reanimated / react-native-gesture-handler composants Fabric. Correctif : Épingler react-native-reanimated à la version compatible avec RN (ex. RN 0.74 → Reanimated 3.10.x). Assurer newArchEnabled=true dans gradle.properties et Podfile use_frameworks! :linkage => :static cohérent. Récupération : Désactiver temporairement Fabric pour le composant : UIManager.setViewManagerConfigurationDescription('MyView', null) en JS. ### Erreurs Worklet Reanimated Symptôme : WorkletError: "worklet" is not defined ou ReanimatedError: [Reanimated] Mismatch . Cause : Ordre d'import ( react-native-gesture-handler doit être premier), babel-plugin-reanimated manquant, ou incompatibilité Hermes/JSC. Correctif : // index.js ou App.js — tout premier import import 'react-native-gesture-handler'; Vérifier babel.config.js : plugins: [ ['react-native-reanimated/plugin', { /* options */ }], ], Attendu : Aucune erreur worklet ; animations s'exécutent sur thread UI. Échec : Oublier de vider le cache Metro après modification config babel. ## Réseau et SSL en profondeur ### Débogage appareil avec adb reverse # Metro bundler adb reverse tcp:8081 tcp:8081 # Serveur API local adb reverse tcp:3000 tcp:3000 # Vérifier adb reverse --list ### Contournement SSL Pinning Charles / Proxyman - Installer CA sur appareil/émulateur : adb push charles-ssl-proxying-certificate.pem /sdcard/Download/ puis Paramètres → Sécurité → Installer depuis carte SD. - Ou utiliser network_security_config.xml (debug uniquement) : - Référencer dans AndroidManifest.xml (variante debug uniquement via android:networkSecurityConfig="@xml/network_security_config" ). ### Exceptions ATS iOS (Debug uniquement) Utiliser xcconfig par configuration : // Debug.xcconfig NSAppTransportSecurity = { NSExceptionDomains = { "localhost" = { NSExceptionAllowsInsecureHTTPLoads = YES; NSIncludesSubdomains = YES; }; }; }; Release.xcconfig : Aucune exception ; imposer HTTPS. ### Contournement Certificate Pinning (Tests uniquement) Bibliothèques : react-native-ssl-pinning (Android), TrustKit (iOS). Ne jamais livrer de contournement pinning en production. Utiliser uniquement pour QA contre staging avec certificats connus. ### Délais par défaut Fetch vs Axios vs Ky Recommandation : Envelopper tous les appels réseau avec un délai de 10–15s et logique de réessai (backoff exponentiel, max 3 réessais). - fetch : Aucun délai par défaut ; implémenter AbortController avec setTimeout . - axios : Défaut 0 (pas de délai) ; définir timeout: 10000 dans config instance. - ky : Défaut 10s ; configurable via option timeout . ## Facturation et paiements (développé) ### Google Play Billing ( react-native-iap / react-native-purchases ) ITEM_UNAVAILABLE / SKU non trouvé : - Vérifier correspondance exacte ID produit dans Play Console (sensible à la casse). - Produit doit être Actif, pas « Inactif » ou « Brouillon ». - Compte testeur doit être dans la liste Testeurs de licence (pas seulement piste de test interne). - Utiliser la piste de test interne pour propagation la plus rapide (minutes vs heures). BillingClient pas prêt : import { initConnection, getProducts, purchaseProduct } from 'react-native-iap'; await initConnection(); // Attendre ready const products = await getProducts({ skus: ['premium_monthly'] }); Vérification : adb logcat -s BillingClient:V IabHelper:V montre onBillingSetupFinished avec OK. ### RevenueCat / react-native-purchases Erreurs courantes : - Purchases.configure appelé plusieurs fois ou avant AppRegistry . - Incohérence calcul droit : customerInfo.entitlements.active['pro'] vs all['pro'] . - logIn / logOut non équilibrés ; IDs anonymes fuient entre utilisateurs. - restorePurchases échoue sur simulateur (nécessite appareil). Modèle de configuration : import Purchases from 'react-native-purchases'; Purchases.configure({ apiKey: Platform.select({ ios: 'appl_...', android: 'goog_...' }), useAmazon: false, observerMode: false, // true seulement si vous gérez les achats vous-même }); // Vérification droit const customerInfo = await Purchases.getCustomerInfo(); const isPro = customerInfo.entitlements.active.pro !== undefined; Vérification côté serveur : Appeler l'API REST RevenueCat /v1/subscribers/{app_user_id} depuis votre backend pour valider les droits avant d'accorder l'accès. ### Apple StoreKit 2 / App Store Connect - Tester avec fichier de configuration StoreKit ( .storekit ) dans Xcode pour test local sans réseau. - Transaction.currentEntitlements (iOS 15+) pour vérification sur appareil. - Vérification reçu côté serveur : POST https://api.revenuecat.com/v1/subscribers/{id} ou endpoint Apple /verifyReceipt (déprécié, utiliser App Store Server API). ## Performance et profilage
OutilObjectifCommande / Configuration
react-native-performance (Sentry)Vitals, frames lentes, TTInpm i @sentry/react-native + Sentry.init({ dsn, enablePerformance: true })
why-did-you-renderRe-rendus inutilesnpm i @welldone-software/why-did-you-render + patcher React dans App.js
hermes-profile / chrome://tracingProfilage CPUadb shell setprop debug.hermes.profiler 1 → charger trace dans Chrome
metro-bundle-analyzerTaille bundlenpx metro-bundle-analyzer → ouvrir rapport HTML
react-native-reanimated profileurTemps exécution workletReanimated.setProfilingEnabled(true) en dev
Profilage rapide : adb shell setprop debug.hermes.profiler 1 , reproduire scénario, adb shell setprop debug.hermes.profiler 0 , récupérer trace : adb shell run-as com.myapp cat files/.hermes/*.trace | npx metro-symbolicate > profile.json , ouvrir chrome://tracing → Charger. ## CI/CD et signaux d'automatisation ### Téléversement artefacts GitHub Actions - name: Upload Gradle scan uses: actions/upload-artifact@v4 if: always() with: name: gradle-scan-${{ github.run_id }} path: android/build/reports/scan/ - name: Upload Xcode result bundle uses: actions/upload-artifact@v4 if: always() with: name: xcode-result-${{ github.run_id }} path: build.xcresult ### Lien Gradle Scan ./gradlew assembleRelease --no-daemon --scan -PreactNativeArchitectures=arm64-v8a # Sortie : "Publishing build scan... https://gradle.com/s/xxxx" ### Bundle de résultats Xcode xcodebuild -workspace MyApp.xcworkspace \ -scheme MyApp \ -configuration Release \ -destination generic/platform=iOS \ -resultBundlePath build.xcresult \ -quiet # Analyser avec xcparse ou xcresulttool ### Journaux EAS Build eas build:list --platform=android --limit=5 eas build:view --logs ### Sortie Fastlane Scan/Gym # Fastfile lane :test do scan( workspace: "MyApp.xcworkspace", scheme: "MyApp", result_bundle: true, output_directory: "fastlane/test_output" ) end ## Annulation et récupération (développé) ### Annulation Expo Updates # Lister mises à jour récentes npx expo-updates:list # Annuler vers précédente npx expo-updates:rollback # Ou publier nouvelle mise à jour avec bundle précédent eas update --branch production --message "Rollback vers v1.2.3" ### Reconstruction EAS Build depuis cache eas build --profile production --platform android --clear-cache # Ou reconstruire commit spécifique eas build --profile production --platform android --commit ### Automatisation Git Bisect # Bisect automatisé pour régression git bisect start HEAD git bisect run npm ci && npm test git bisect reset ### Isolation reconstruction module natif # Réinstaller deps JS sans scripts de rebuild natif npm ci --ignore-scripts # Puis reconstruire natif uniquement cd android && ./gradlew clean assembleDebug cd ../ios && pod install --repo-update ## Scénario technique réaliste Scénario : Build release Android crashe au démarrage avec java.lang.UnsatisfiedLinkError: libhermes.so après mise à niveau RN 0.73→0.74 et ajout de react-native-reanimated 3.x. ### 1. Capture d'inventaire npx react-native info # Sortie montre : RN 0.74.2, Hermes activé, AGP 8.2.2, JDK 21, Reanimated 3.10.0 ### 2. Corrélation des journaux # logcat pour crash natif adb logcat -s ReactNative:V System.err:V -v threadtime > crash.log # Vidage crash Hermes adb shell run-as com.myapp cat files/.hermes/*.log | npx metro-symbolicate > hermes-crash.txt Lignes logcat clés : E System.err: java.lang.UnsatisfiedLinkError: dlopen failed: library "libhermes.so" not found E System.err: at com.facebook.soloader.SoLoader.loadLibrary(SoLoader.java:...) E ReactNativeJS: Fatal: TurboModuleRegistry.getEnforcing(...): 'NativeReanimated' non trouvé ### 3. Portée réduite - Désactiver Reanimated : Commenter import 'react-native-reanimated'; et utilisation Reanimated. - Rebuild : cd android && ./gradlew clean assembleRelease --stacktrace . - Résultat : App se lance → Reanimated est le déclencheur. ### 4. Cause racine et correctif Cause : RN 0.74 requiert react-native-reanimated 3.10+ avec Fabric activé. libhermes.so manquant car hermes_enabled=true mais newArchEnabled=false dans gradle.properties — liaisons JSI Hermes pour TurboModules non générées. Config soLoader dans android/app/build.gradle manquante jniLibs pour Hermes. Correctif ( android/gradle.properties ) : hermes_enabled=true newArchEnabled=true # Requis pour TurboModules/Fabric Correctif ( android/app/build.gradle ) : dependencies { // Assurer empaquetage libs JSI Hermes implementation "com.facebook.react:hermes-engine:+" } Correctif ( ios/Podfile ) : use_frameworks! :linkage => :static # Assurer pods Reanimated utilisent Fabric pod 'React-RCTFabric', :path => '../node_modules/react-native/React/Fabric' Exécuter : cd android && ./gradlew clean assembleRelease --no-daemon --scan -PreactNativeArchitectures=arm64-v8a cd ../ios && pod install --repo-update ### 5. Vérifier - APK release s'installe et se lance sur appareil. - adb logcat -s ReactNative:V montre succès TurboModuleRegistry.getEnforcing: NativeReanimated . - Animations Reanimated s'exécutent sur thread UI (pas de chutes thread JS). ### 6. Plan d'annulation # Annuler gradle.properties git checkout HEAD -- android/gradle.properties # Annuler version Reanimated npm install react-native-reanimated@3.6.2 # dernière version compatible RN 0.73 # Rebuild cd android && ./gradlew clean assembleRelease Décision : Si délai critique, annuler RN vers 0.73.3 et Reanimated vers 3.6.2 ; planifier migration RN 0.74 avec QA dédiée. ## Conclusion Le dépannage React Native est le plus efficace lorsque vous stabilisez l'environnement, rassemblez des journaux à fort signal et appliquez le plus petit changement sûr. Avec un inventaire clair, des paramètres de debug et release prévisibles, et une routine disciplinée de vérification et d'annulation, la plupart des problèmes deviennent simples à isoler et corriger. Utilisez les commandes d'inventaire pour établir une base de référence, les commandes de journaux pour observer le comportement réel sur Metro, logcat, OSLog et vidages crash Hermes, et les correctifs pilotés par exemples ici pour résoudre les problèmes courants de résolution Metro, builds Android et iOS, mise en réseau REST, Google Play Billing, Stripe et intégrations RevenueCat. Gardez les changements petits, vérifiez localement sur les deux plateformes, et annulez rapidement si nécessaire. Avec le temps, cette approche raccourcit la durée des incidents et rend les résultats fiables dans votre équipe.