1 février 2023
Performances avec React Native
5 minutes de lecture
Au cours du développement de nos applications mobiles, nous effectuons de nombreux tests et recettes. Cependant ils sont bien souvent effectués en mode de développement, sur un simulateur ou nos téléphones de dernière génération. Les performances de notre application ne sont pas représentatives de ce qu'elles seront en production, sur une multitude de téléphones et de réseaux.
Je vous propose dans cet article de regarder dans notre boite à outils, ce qu'il est possible d'utiliser pour détecter et analyser la performance d'une application React Native, ainsi que les solutions envisageables pour l'améliorer.
Les indices à suivre
Généralement les soucis de performances n'apparaissent pas directement lors du développement mais plutôt en phase de recette ou de production. Voici quelques indices qui peuvent vous aider à trouver des problèmes lors de l'utilisation :
- Temps de démarrage de l'application long, généralement lié à la taille du bundle ou à des requêtes réseau bloquantes
- L'application ne "semble" pas fluide
- Clic sur des éléments prend du retard
- Perte d'images sur les transitions ou le scroll
- Et enfin les métriques de bases concernant les ressources (accessible via le debugger React Native)
- CPU qui doit rarement atteindre les 100% et surtout ne pas y rester
- FPS qui doivent être autant que possible dans les 60
Notre boite à outils
Grâce à la communauté et l'écosystème JavaScript, on dispose de nombreux logiciels pour détecter les problèmes de performance dans une application React Native. Néanmoins je vous propose un premier outil physique.
Un ancien téléphone
Reprenez ce vieux téléphone Samsung que vous avez passé à votre grand-mère, ou encore cet iPhone que vous avez dans votre tiroir, et essayez votre dernière application dessus. Observez que l'expérience utilisateur n'est pas la même (et c'est normal), mais grâce à ce téléphone moins performant que ceux que vous avez l'habitude d'utiliser, vous pouvez directement identifier les problèmes de performance.
Logger, un peu, beaucoup, à la folie
On monte un peu en gamme avec les logs. Ils sont très utiles pour identifier du code exécuté plusieurs fois (ou trop souvent, I see you React) ou encore débugger des requêtes.
C'est cependant un outil limité, et j'ai tendance à privilégier l'instruction debugger
ou les points d'arrêts pour prendre le temps d'analyser la stack d'appels et les variables (oui ça nous bloque, à éviter dans des boucles, et ne pas oublier d'utiliser les raccourcis clavier comme F8
dans Chrome pour passer le breakpoint).
Vous connaissez sûrement console.log
, mais j'apprécie particulièrement console.debug
pour pouvoir isoler plus facilement des autres logs applicatifs, ainsi que console.count()
pour compter le nombre d'appels d'une fonction, ou encore console.time
pour afficher des temps d'exécutions.
Flipper, un compagnon de route
Flipper est un outil développé par Facebook, qui permet de debugger et d'analyser une application React Native. Il permet via l'intégration de plugins de visualiser les logs, les requêtes réseau, les performances, et bien d'autres choses encore. Vous l'aurez compris c'est l'outil à connaître et à maîtriser autant que possible.
Sa mise en place peut être un peu fastidieuse, selon le contexte de votre application, mais je vous invite à suivre la documentation officielle. Si vous rencontrez des soucis de détection de l'app, faites bien attention à synchroniser les versions de Flipper et du package react-native-flipper
.
J'apprécie particulièrement l'analyse des requêtes réseau qui permet d'identifier des requêtes en doublon, trop longue ou encore avec des données non utilisées.
React DevTools
Le classique inspecteur de React est disponible via les plugins Flipper, et concernant les performances, la partie Profiler va pouvoir s'avérer très utile !
Par exemple on peut facilement identifier des rendus long ou trop fréquents, et ainsi les optimiser. Je recommande de cacher les rendus de moins de 10ms pour éviter trop de bruits sur les petits composants.
Cet outil nous a souvent permis d'optimiser certains comportements via les bonnes pratiques React (attention à n'utiliser que en cas de soucis de performances pour éviter la sur-optimisation).
React.memo
pour mémoïser les composantsuseCallback
pour éviter de créer des fonctions à chaque renduuseMemo
pour éviter de recalculer des valeurs à chaque renduuseRef
pour des états qui ne déclenchent pas de rendu
react-native-flipper-performance-monitor
Sur le web nous utilisons régulièrement Lighthouse pour faire un audit très rapide de la performance réelle et perçue d'une application web. Dans le monde mobile il existe de nombreux outils mais rarement facile à mettre en oeuvre et avec un rendu immédiat.
react-native-flipper-performance-monitor permet cependant d'obtenir un score de performance en quelques secondes, et de visualiser les problèmes de performance directement dans Flipper via l'ajout du plugin. On peut voir ici un exemple concret avant / après optimisation, d'une application qui utilisait énormément de CPU alors qu'elle était dans un état inactive, l'origine du problème était un composant qui utilisait un setInterval
pour faire un setState
toutes les secondes et de nombreux composants en dépendaient.
Sur le long terme
J'ai noté d'autres outils qui peuvent être ajoutés à votre trousse, cependant nous ne les avons pas encore expérimentés ou mis en place, ils restent cependant assez intéressants si vous souhaitez tracker les évolutions de performances sur le long terme.
- reassure propose d'écrire des tests de performances, en utilisant React Native Testing Library et de tracker ainsi les temps et nombres de rendus des composants
- react-native-performance permet de tracker plusieurs indicateurs via l'ajout de code natif mais aussi JavaScript au niveau des composants ou encore de la navigation. On a ainsi accès à des évènements très riches pour suivre le temps de démarrage de l'application, le temps de rendu des composants, la navigation ou encore les listes.
- Sentry, que vous utilisez peut-être déjà pour le tracking des erreurs, permet aussi de suivre les performances, de manière assez similaire à ce que propose la solution react-native-performance, mais cette fois avec le reporting déjà géré et via une solution Saas.
Pistes d'améliorations
Nous avons eu l'occasion via la réalisation de nombreuse applications de résoudre des problématiques de performances. En voici une liste non exhaustive :
- Rerendu fréquent via l'utilisation abusive de contextes, cf https://www.premieroctet.com/blog/contextes-et-performances
- Overfetching d'API, n'utiliser que les données nécessaires (GraphQL, groupe de sérialisation API platform...)
- Utilisation d'un cache local, ex react-query
- Taille des images locales et distantes
- Des dépendances peu performantes, exemple avec un UI kit comme NativeBase, cf https://www.premieroctet.com/blog/performances-native-base
- Ne charger les données qu'à la visibilité du composant
Conclusion
Cet article contient des pistes et des recherches personnelles, ainsi que de nombreux outils et solutions utilisés chez Premier Octet. J'espère qu'il vous a apporté des idées pour améliorer les performances de vos applications.
Pour aller plus loin, je vous recommande l'excellent guide récemment publié par CallStack qui est une mine d'or d'informations sur les optimisations possibles avec React Native.