Au fil des années, React a su gagner en popularité, notamment grâce à sa modularité et sa simplicité d’utilisation.
Actuellement, Facebook teste des fonctionnalités en mode expérimental parmi lesquelles le mode concurrent. Il s'agit d'un ensemble de nouvelles fonctionnalités qui aident les applications React à rester réactives et à s’adapter de façon fluide aux capacités et au débit réseau de l’appareil de l’utilisateur.
Le mode concurrent, qu'est-ce que c'est ?
Pour expliquer le mode concurrent, Facebook s'est servi de la gestion de versions comme métaphore. Si vous travaillez en équipe, vous utilisez probablement un système de gestion de versions tel que Git, et travaillez sur des branches. Quand une branche est finalisée, vous pouvez en fusionner le travail dans master afin que d’autres puissent le récupérer.
Avant que la gestion de versions n’apparaisse, les flux de développement étaient très différents. On n’y trouvait aucun concept de branche. Si vous aviez besoin de modifier certains fichiers, il fallait prévenir tout le monde de ne pas y toucher pendant ce temps-là. Vous ne pouviez même pas commencer à travailler dessus en parallèle les uns des autres : vous étiez littéralement bloqué(e) par l’autre personne.
Ce scénario illustre bien la façon dont les bibliothèques d’interface utilisateur (UI) fonctionnent généralement aujourd’hui. Une fois qu’elles démarrent le rendu d’une mise à jour, y compris la création de nouveaux nœuds DOM et l’exécution du code au sein des composants, elles ne peuvent pas être interrompues. Facebook appelle cette approche le « rendu bloquant ».
Avec le mode concurrent, le rendu n’est pas bloquant : il est interruptible. Cela améliore l’expérience utilisateur et en prime cela ouvre la porte à de nouvelles fonctionnalités qui étaient impossibles jusque-là.
Remplace ReactDOM.render(<App />, rootNode) et active le mode concurrent
Rendu interruptible
Prenez une liste de produits filtrable. Vous est-il déjà arrivé de taper dans le champ de filtrage pour ressentir un affichage saccadé à chaque touche pressée ? Une partie du travail de mise à jour de la liste de produits est peut-être incontournable, telle que la création des nouveaux nœuds DOM ou la mise en page effectuée par le navigateur. En revanche, le moment d’exécution de ce travail et la manière dont il est exécuté jouent un rôle crucial.
Une manière courante de contourner ces saccades consiste à « debouncer » la saisie dans le champ. En lissant ainsi le traitement de la saisie, nous ne mettons à jour la liste qu’après que l’utilisateur a cessé de saisir sa valeur. Cependant, il peut être frustrant de ne constater aucune mise à jour de l’UI lors de la frappe. On pourrait plutôt « ralentir » (throttle, NdT) la gestion de la saisie, et ne mettre à jour la liste qu’à hauteur d’une fréquence maximale définie. Mais sur des appareils de faible puissance, nous constaterions toujours une saccade. Tant le debouncing que le throttling aboutissent à une expérience utilisateur sous-optimale.
La raison de cette saccade est simple : une fois que le rendu commence, il ne peut être interrompu. Ainsi le navigateur ne peut plus mettre à jour le texte dans le champ de saisie juste après que vous avez pressé une touche. Peu importent les scores mirifiques que votre bibliothèque UI (telle que React) obtient dans tel ou tel comparatif, si elle recourt à un rendu bloquant, à partir d’une certaine charge de travail sur vos composants vous obtiendrez toujours un affichage saccadé. Et la plupart du temps, il n’existe pas de correctif simple.
Remplace ReactDOM.render(<App />, rootNode) at active le mode bloquant.
Le mode concurrent corrige cette limitation fondamentale en utilisant un rendu interruptible. Ça signifie que lorsque l’utilisateur presse une touche, React n’a pas besoin d’empêcher le navigateur de mettre à jour le champ de saisie. Il va plutôt laisser le navigateur afficher cette mise à jour, et continuer le rendu de la liste à jour en mémoire. Quand le rendu sera fini, React mettra à jour le DOM, et les modifications seront ainsi reflétées à l’écran.
Conceptuellement, vous pouvez imaginer que React prépare chaque mise à jour « sur une branche ». Tout comme vous êtes libre d’abandonner le travail d’une branche ou de passer d’une branche à l’autre, React en mode concurrent peut interrompre une mise à jour en cours afin de prioriser une tâche plus critique, puis revenir à ce qu’il était en train de faire. Cette technique n’est pas sans rappeler le double buffering des jeux vidéos.
Les techniques du mode concurrent réduisent le besoin de debouncing et de throttling dans l’UI. Le rendu étant interruptible, React n’a plus besoin de différer artificiellement du travail afin d’éviter les saccades. Il peut commencer le rendu immédiatement, et interrompre ce travail si nécessaire afin de préserver la réactivité de l’appli.
Suspense permet à vos composants « d’attendre » que quelque chose ait lieu avant qu’ils procèdent à leur rendu, en affichant dans l’intervalle une interface utilisateur (UI) de repli
Séquences de chargement intentionnelles
Nous disions plus haut que pour comprendre le mode concurrent, on peut imaginer que React travaille « sur une branche ». Les branches ne sont pas seulement utiles pour des correctifs à court terme, mais aussi pour des fonctionnalités plus longues à écrire. Parfois vous pouvez travailler sur une fonctionnalité qui va mettre des semaines avant d’être « assez finie » pour être fusionnée dans master. Ici aussi, la métaphore de la gestion de versions s’applique bien au rendu.
Imaginez que vous naviguiez entre deux écrans d’une appli. Parfois, nous n’aurons peut-être pas assez de code et de données pour afficher un état de chargement « assez fini » à l’utilisateur au sein du nouvel écran. Transiter vers un écran vide (ou doté d’un gros spinner) n’est pas une expérience agréable. Et pourtant, il arrive fréquemment que les chargements du code et des données nécessaires ne prennent en fait que peu de temps. Ne serait-il pas plus agréable que React puisse rester sur l’ancien écran un tout petit peu plus longtemps, pour ensuite « sauter » l’état de « chargement désagréable » lors de la bascule vers le nouvel écran ?
C’est certes possible aujourd’hui, mais au prix d’une orchestration délicate. Avec le mode concurrent, cette fonctionnalité est directement disponible. React commence à préparer le nouvel écran en mémoire d’abord — ou, pour revenir à notre métaphore, « sur une autre branche ». Ainsi, React peut attendre que davantage de contenu ait été chargé avant de mettre à jour le DOM. Avec le mode concurrent, nous pouvons dire à React de continuer à afficher l’ancien écran, pleinement interactif, avec peut-être un indicateur de chargement dans un coin. Et lorsque le nouvel écran est prêt, React peut nous y amener.
Concurrence
Résumons les deux exemples ci-avant pour voir comment le mode concurrent en unifie le traitement. Avec le mode concurrent, React peut travailler à plusieurs mises à jour de l’état en exécution concurrente, tout comme les branches permettent à divers membres d’une équipe de travailler indépendamment les uns des autres :
- Pour les mises à jour dépendantes du processeur (CPU, telles que la création des nœuds DOM et l’exécution du code des composants), la concurrence permet à une mise à jour plus urgente « d’interrompre » le rendu qui a déjà démarré.
- Pour les mises à jour dépendantes des entrées/sorties (I/O, telles que le chargement de code ou de données à partir du réseau), la concurrence permet à React de commencer le rendu en mémoire avant même que les données n’arrivent, et de sauter des états de chargement désagréables.
Ce qui est critique, c’est que la façon dont vous utilisez React reste inchangée. Les concepts tels que les composants, les props et l’état local continuent fondamentalement à marcher de la même façon. Quand vous voulez mettre à jour l’écran, vous ajustez l’état.
React utilise des heuristiques pour déterminer le degré « d’urgence » d’une mise à jour, et vous permet d’ajuster ces choix au moyen de quelques lignes de code pour aboutir à l’expérience utilisateur que vous souhaitez suite à chaque interaction.
Bénéficier de la recherche dans la production
Les fonctionnalités du mode concurrent ont toutes le même objectif. Leur mission consiste à faire bénéficier de véritables UI des dernières trouvailles de la recherche en Interactions Humain-Machine.
Par exemple, la recherche montre qu’afficher trop d’états de chargement intermédiaires lors d’une transition entre écrans entraîne un sentiment accru de lenteur. C’est pourquoi le mode concurrent n’affiche de nouveaux états de chargement que selon un « planning » fixe afin d’éviter des mises à jour trop fréquentes ou désagréables.
Dans le même esprit, la recherche nous dit que des interactions telles que le survol du pointeur ou la saisie de texte doivent être traitées dans un très court laps de temps, alors que les clics et les transitions de pages peuvent durer un peu plus longtemps sans pour autant sembler lents. Les différentes « priorités » que le mode concurrent utilise en interne correspondent à peu près aux catégories d’interactions qu’on peut trouver dans la recherche en perception humaine.
Les équipes accordant une importance primordiale à l’expérience utilisateur résolvent parfois ce type de problème avec une solution ad hoc. Néanmoins, ces solutions survivent rarement à l’épreuve du temps, et sont difficiles à maintenir. Avec le mode concurrent, nous tentons de condenser les résultats de la recherche UI directement dans l’abstraction proposée par React, et d’en rendre l’utilisation idiomatique. React, en tant que bibliothèque UI, est bien placée pour ça.
Source : React
Et vous ?
Utilisez-vous React ? Qu'en pensez-vous ?
Que pensez-vous de ce mode ?
Voir aussi :
Flutter, le framework UI de Google, serait-il meilleur que React Native de Facebook pour le développement d'applications multiplateformes ?
Facebook publie en open source Hermes, un moteur JavaScript léger optimisé pour exécuter React Native sur Android
React : la version 16.8 de la bibliothèque JavaScript est disponible et embarque une version stable des Hooks
Mithril : un framework JavaScript moderne, simple, rapide et léger comparé à React ou Angular pour ceux qui privilégient la facilité d'intégration
Facebook annonce la réécriture des composants internes de son Framework React Native pour faciliter son utilisation avec les applications hybrides