La navigation avec React Native

UNE ASSISTANCE TECHNIQUE ? NOUS SOMMES DISPONIBLES UNE ASSISTANCE TECHNIQUE ? NOUS SOMMES DISPONIBLES

Accéder au sommaire de “Guide pratique du développeur React Native”

Il faut différencier la navigation dans le monde Web et la navigation dans le monde mobile. Le mobile requiert une plus grande vigilance et peut s’avérer plus complexe. La réussite de l’expérience utilisateur repose en grande partie sur une bonne gestion de la navigation. Il existe également une différence de culture entre le monde iOS et Android. Les utilisateurs Apple n’ont pas de bouton “Précédent”, bouton Ô combien indispensable pour les utilisateurs Android.

Comment réconcilier deux mondes différents lorsque React Native ne propose qu’un seul code source commun ? Comment gérer la navigation de telle sorte qu’elle soit naturelle et se fonde dans le système hôte ?

Je prendrai encore une fois comme exemple la navigation dans Dossardeur. Dossardeur utilise un Drawer (un menu tiroir), des onglets (Tab Navigator), des écrans (tunnels ou isolé) et un entête et pied de page affichant des informations contextuelles. La navigation dans Dossardeur est le fruit d’un long travail d’analyse. Après avoir épluché des dizaines d’applications telles que Strava, Leboncoin ou Too good to go, il a fallu identifier quelques bonnes pratiques :

  • Eviter la navigation spaguetti (lorsque trop d’écrans proposent des menus pour aller vers tous les autres écrans)
  • Identifier et gérer au mieux les boucles de navigation (Ecran1 => Ecran2 => Ecran1 => Ecran2 etc …)
  • Vider l’historique de navigation lorsque l’état précédent n’a plus de sens (voir paramètres reset et replace)
  • Gérer les écrans uniques (on évitera par exemple de garder un écran de paramétrage dans l’historique avec potentiellement des données obsolètes)
  • Gérer les informations contextuelles d’un écran comme l’utilisateur courant connecté
  • Afficher/Masquer le menu tiroir principal en fonction du contexte courant

Une bonne navigation est une navigation intelligente, fluide, simple et surtout transparente. Celle qui se fait oublier.

Navigation Web != Navigation Mobile

Sur le Web, lorsque vous cliquez sur les différents liens d’une page contenant le tag (<a>), votre navigateur empile les pages que vous visitez dans l’historique de navigation. Lorsque vous appuyez sur le bouton précédent, la dernière page en cache est chargée et remplace la page courante. Ce mécanisme n’existe pas en React Native, c’est tout le rôle d’outils tels que React Navigation ou React Router.

React Router

React Router est plus un Framework de routage qu’un Framework de navigation. Lorsque vous souhaitez maîtriser vous même les composants graphiques de navigation (en y apportant par exemple une touche personnelle) et bénéficier des notions d’historique, d’URL et de paramètres, React Router est un bon choix.

Le coeur de React Router est conçu de manière agnostique, il existe ensuite une version dédiée au Web (react-router-dom) et une version dédié à React Native (react-router-native).

Vous trouverez sur le site de React Router des exemples de code.

React Navigation

React Navigation est un outil indispensable lorsqu’on souhaite construire une application avec une identité visuelle proche du système d’exploitation. React Navigation fournit de nombreuses fonctionnalités qui reprennent en partie les composants natifs iOS et Android :

  • Gestion de l’historique de navigation
  • Cycle de vie de navigation
  • Fenêtre modales
  • Navigation imbriquées (par exemple un tunnel d’authentification)
  • Gestion des onglets et des menus tiroirs (Drawers)
  • Gestion des thèmes (clair, sombre)
  • Personnalisation des composants d’en tête et pied de page

Cet article ne traite pas des fonctionnalités de React Navigation mais plutôt des pièges à éviter, n’hésitez pas à vous référer à la documentation officielle pour plus de détails.

React Navigation est très riche et parfois complexe. Il avouer que mes premières expériences avec RN ont plutôt été douloureux. Lorsque vous avez l’habitude de gérer vous même le cycle de vie des écrans, React Navigation peut s’avérer destabilisant.

J’ai une anecdote à ce sujet qui illustre bien les dangers de React Navigation lorsqu’on ne maîtrise pas un minimum son fonctionnement. Sur une application donnée, nous avions un écran qui utilisait la caméra du mobile. Une fois sorti de cet écran, le fil continuait sur des écrans assez classiques avec la possibilité à tout moment de revenir sur la caméra. Or, après quelques minutes d’utilisation, nous nous sommes aperçus que le composant natif Android de la caméra provoquait des crash aléatoires. Et pour cause, voici ce que React Navigation précise dans sa documentation :

“Consider a stack navigator with screens A and B. After navigating to A, its componentDidMount is called. When pushing B, its componentDidMount is also called, but A remains mounted on the stack and its componentWillUnmount is therefore not called. When going back from B to A, componentWillUnmount of B is called, but componentDidMount of A is not because A remained mounted the whole time. While React’s lifecycle methods are still valid, React Navigation adds more events that you can subscribe to through useFocusEffect or useIsFocused hooks”

Le fonctionnement de React Navigation consiste à mettre les composants en cache sans jamais les démonter lorsqu’un écran est appelé avec l’ordre navigation.push().  De plus, si vous avez un écran dans votre historique dont une des propriétés est liée à l’état global (Redux, mobX ou Context), React Navigation s’assure que son état est synchronisé. Concrètement, la méthode render() de l’écran contenant notre composant Caméra était appelée en permanence au gré de l’évolution de l’état (!). Or, certains composants natifs comme la caméra doivent être instanciés une et une seule fois, d’où les instabilités.

C’est comme si vous aviez sur le Web des pages en cache dont le contenu était synchronisé avec l’état serveur, ce serait plutôt déroutant car c’est un historique sans être un cache. Avec React Navigation, oubliez donc le cycle de vie habituel des composants React, seul useFocusEffect() vous indiquera si un écran monté est visible. La nuance est importante et les performances peuvent être désastreuses si vous ne maîtrisez pas cette subtilité.

Navigation.push() vs Navigation.navigate()  

Voilà encore un autre piège de React Navigation. Lorsqu’on navigue vers un écran, deux options sont proposées : empiler l’écran dans l’historique avec l’ordre navigation.push ou naviguer vers la seule instance de l’écran avec l’ordre navigation.navigate.  Voici ce que précise la documentation à ce sujet :

“navigation.navigate(‘RouteName’) pushes a new route to the stack navigator if it’s not already in the stack, otherwise it jumps to that screen. We can call navigation.push(‘RouteName’) as many times as we like and it will continue pushing routes”

Comment savoir si l’on souhaite précisément appeler navigate ou push ? Tout dépend de la nature des données que vous souhaitez empiler dans l’historique. Certains écrans n’ont pas de pertinence lorsqu’ils sont en double dans l’historique. Seul le dernier état de l’écran compte. Prenez l’exemple du panier sur un site de e-commerce, vous ne souhaitez surtout pas qu’un utilisateur puisse voir plusieurs versions de l’écran Panier contenant plusieurs produits différents ? Il serait instantanément pris de doutes. Voici pourquoi l’écran Panier doit être représenté par une seule version avec un seul état. En revanche, prenez l’écran des palmarès dans Dossardeur. Lorsque l’utilisateur clic sur un sportif dans le classement d’une course, il accède au palmarès du coureur en question. Il est possible d’entrer dans un tunnel interminable dans lequel l’utilisateur a commencé avec le classement d’une course A pour finir sur le classement d’une course B car dans le palmarès du coureur se trouve des courses vers lesquels il peut également naviguer. Ce mécanisme lui permet d’empiler toute une série d’épreuves et classements au gré de ses envies sans jamais perdre le fil. Un peu comme lorsqu’on se perd dans les classements des segments de l’application Strava.

C’est tout l’enjeu de la navigation, trouver le bon compromis entre navigate et push tout en donnant un cadre cohérent. N’oubliez pas que sur iOS, les utilisateurs n’ont pas de bouton “Précédent”, il faut donc systématiquement afficher un bouton précédent à l’écran. Et lorsque le tunnel de navigation est trop long, permettre à ceux qui le souhaitent de remonter rapidement le fil.

Bibliographie

Choosing a Routing Library : Excellent article pour vous aider à faire un choix

React Navigation vs. React Native Navigation: Which is right for you?

UNE ASSISTANCE TECHNIQUE ? NOUS SOMMES DISPONIBLES UNE ASSISTANCE TECHNIQUE ? NOUS SOMMES DISPONIBLES