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érencenpx react-native start --reset-cache— vider le cache Metrocd android && ./gradlew clean assembleDebug --stacktrace— build Android proprecd ios && pod deintegrate && pod install && cd ..— réinitialiser les pods iOSrm -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/<pkg>/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 travail | Android | iOS | Notes |
|---|---|---|---|
| Bare | cd android && ./gradlew clean assembleDebug --stacktrace | cd ios && pod deintegrate && pod install --repo-update && cd .. | Exécuter adb reverse tcp:8081 tcp:8081 pour débogage appareil |
| Expo Managed | npx expo prebuild --clean --platform android puis cd android && ./gradlew clean assembleDebug | npx expo prebuild --clean --platform ios puis cd ios && pod install --repo-update | npx expo start -c vide le cache Metro |
| Expo Dev Client | eas build --profile development --platform android --local | eas build --profile development --platform ios --local | Journaux client dev via adb logcat / Console Xcode |
| Tous | npx react-native start --reset-cache | npx react-native start --reset-cache | Réinitialisation cache Metro universelle |
| Réinitialisation simulateur | adb emu kill + cold boot | xcrun simctl erase all | Option 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) :
<!-- android/app/src/debug/res/xml/network_security_config.xml -->
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
<debug-overrides>
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</debug-overrides>
</network-security-config>
- Référencer dans
AndroidManifest.xml(variante debug uniquement viaandroid: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émenterAbortControlleravecsetTimeout.axios: Défaut 0 (pas de délai) ; définirtimeout: 10000dans config instance.ky: Défaut 10s ; configurable via optiontimeout.
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.configureappelé plusieurs fois ou avantAppRegistry.- Incohérence calcul droit :
customerInfo.entitlements.active['pro']vsall['pro']. logIn/logOutnon é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
| Outil | Objectif | Commande / Configuration |
|---|---|---|
react-native-performance (Sentry) | Vitals, frames lentes, TTI | npm i @sentry/react-native + Sentry.init({ dsn, enablePerformance: true }) |
why-did-you-render | Re-rendus inutiles | npm i @welldone-software/why-did-you-render + patcher React dans App.js |
hermes-profile / chrome://tracing | Profilage CPU | adb shell setprop debug.hermes.profiler 1 → charger trace dans Chrome |
metro-bundle-analyzer | Taille bundle | npx metro-bundle-analyzer → ouvrir rapport HTML |
react-native-reanimated profileur | Temps exécution worklet | Reanimated.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 <build-id> --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 <sha>
Automatisation Git Bisect
# Bisect automatisé pour régression
git bisect start HEAD <bon-tag>
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:Vmontre succèsTurboModuleRegistry.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 [email protected] # 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.