:::: MENU ::::

Pourquoi vous devriez utiliser React

S’il y a bien une techno à la mode ces derniers temps c’est ReactJs. Open source, conçue et poussée par Facebook, elle se présente comme « une librairie pour construire des interface graphiques ».

Simple mode ? Réel changement ? Après avoir dépassé le cap fatidique des 2000 watchers sur AngularJs et constaté ses limitations en terme de rendu pour des composants graphiques complexes, c’est principalement pour un problème de performance que j’ai voulu tester React. L’excuse était trouvée…Voici donc le debrief d’une semaine passée avec la bête, avec le point de vue d’un utilisateur d’AngularJs

React fait peur  : c’est du javascript

A première vue, React peut faire un peu peur :

Un todo minimaliste avec React

Une application de Todoliste minimaliste avec React

Quand on est habitué à la magie d’Angular et de son data-binding, ça fait mal. Désormais, c’est terminé , il va falloir tout repenser ( et c’est une bonne chose ) . Lorsque l’on travail avec ReactJs la première chose à faire c’est oublier : oublier le DOM, les sélecteurs CSS, le HTML… on reste dans le code javascript, même pour des composants qui ont une finalité graphique.

De simple composants

Le composant est une des fondations de React et cela va dans le sens de l’avenir du web

canard automate

 

 

La première chose à faire est de décomposer son application ou son composant graphique en une hiérarchie de sous composants . Plus la découpe est fine, plus les composants seront simples. Evidemment cette découpe doit suivre une certaine logique.

 

 

 

 

Dans l’exemple de la todoliste on peut voir qu’il y a trois composants : le composant principal (TodoApp), la liste des todos (TodoList) et un élément de liste dont la création est gérée par le composant parent TodoList. Avant d’aller plus loin on remarque la présence de nombreux this ainsi qu’un map qui est utilisé pour créer les éléments de todo. Ce n’est que du javascript, le this référençant le composant sur lequel on travail .

En travaillant avec React, vous n’aurez jamais autant fait de pur javascript !

JSX l’incompris

Le concept derrière ReactJs est de travailler sur un DOM virtuel et non sur le vrai DOM dont les accès en lecture et surtout en écriture sont très lent comparés à des traitements en mémoire. Un algorithme se chargera de mettre à jour pour nous le vrai DOM de la manière la plus efficace possible, la finalité restant néanmoins la création et/ou la modification de balises HTML . Pour intégrer un élément dans le DOM, on peut utiliser React.createElement() puis spécifier le type de balise ainsi que ses propriétés, chaque élément pouvant être parent d’autres éléments, comme dans une page HTML.


var child = React.createElement('li', null, 'Text Content');
var root = React.createElement('ul', { className: 'my-list' }, child); 

React.render(root, document.body);

Le problème avec cette méthode d’écriture c’est qu’elle devient vite complexe au fur à mesure que les composants contiennent des éléments. De plus on est loin du concept HTML si familier ce qui peut rendre difficile la compréhension du code pour des non initiés.

Les ptits gars de facebook ont donc créé JSX, une syntaxe pour nous simplifier la vie (enfin une fois qu’on veut bien l’accepter )

gif animé doute

Réaction probable face à JSX si on a pas compris les fondamentaux

Lors des ateliers de SudWeb 2014, il y a eu une discussion/atelier autour de React qui à fortement déviée. Pourquoi ? Car elle introduisait React par JSX et la comparaison avec Angular (j’y reviens plus bas ). C’est souvent le piège quand on s’attaque à React car l’usage de JSX peut dérouter.

JSX n’est pas du HTML

Il faut bien comprendre et faire la différence entre un template (type template de directive ) et JSX. JSX introduit une syntaxe qui ressemble à du HTML mais qui n’en est pas. Il sert uniquement à simplifier l’écriture des composants et disparaîtra au profit de pur javascript ( la syntaxe vue plus haut) après une phase de transformation à l’aide de transpileur  . A noter que JSX n’est pas propre à React et on peut utiliser cette syntaxe pour d’autres technos si on le souhaite. Vous trouverez plus d’infos sur la page de la spécification .

La différence entre la syntaxe pure javascript et JSX est assez parlante et compréhensible :


// Hello World
//JSX
var HelloMessage = React.createClass({
 render: function() {
 return <div>Hello {this.name}</div>;
 }
});

React.render(<HelloMessage name="John" />, mountNode);

//Javascript
var HelloMessage = React.createClass({displayName: 'HelloMessage',
 render: function() {
 return React.createElement("div", null, "Hello ", this.props.name);
 }
});

React.render(React.createElement(HelloMessage, {name: "John"}), mountNode);

Reprenons notre exemple de TodoList avec JSX cette fois ci :

Aperçu du code d'une Todo Liste en JSX

On distingue nettement un markup qui ressemble à quelque chose de connu dans l’élément principal TodoApp. Le composant TodoList possède à l’intérieur de ce qui semble être une balise, des accolades, signe d’un binding évident .

 

Se pose alors la question suivante : Si on travail dans notre javascript pour traiter le DOM et qu’on y introduit du markup , est ce qu’on ne brise pas les couches HTML / CSS / JS (structure / présentation / comportement ) ?

Et bien non si on se souvient que le JSX n’est pas du HTML . Avec React on définit le comportement des éléments , la structure doit être limité de manière atomique et généralement si elle est complexe, c’est que le découpage est mauvais. D’autre part, si l’élément graphique à générer est complexe et dispose d’une structure évolutive, elle nécessitera de toute façon une communication entre les couches de comportement et de structure voire de présentation .

Travailler dans ce sens me semble finalement plus logique que l’inverse comme le font la plupart des systèmes de template  . Ce n’est pas mal en soit mais ça peut le devenir si on se laisse prendre au jeu. On perd alors en maintenabilité

Question workflow, puisque si on utilise JSX on doit passer par une phase de « compilation », en utilisant gulp par exemple c’est presque transparent en pipant gulp-react dans votre flux de sources. Sinon react-tools est un outil fourni par la librairie qui propose un système de watch pour transpiler à la volée.

La Programmation Réactive, puissance ultime pour une IHM efficace ?

Le nom de React n’est surement pas choisi au hasard puisque le modèle de programmation de la librairie utilise le paradigme de la programmation réactive. En gros chaque composant d’interface agit comme une machine à état. A chaque fois que l’état change, le rendu est mis à jour si nécessaire . C’est un style de programmation qui se prête très bien aux interfaces graphiques puisqu’il est généralement possible de définir une représentation statique et plusieurs états de son interface en fonction de différents événements extérieurs .

Dans React la fonction de rendu d’un composant retourne des données statiques qui vont être intégrées au DOM. Ces données sont mis à jour à partir des props ou des states.

Les props sont les propriétés passées à un composant par son créateur. Si on reprend notre exemple, le composant TodoList possède une propriété items qui contient un tableau de todos. Son seul but est d’affiner la liste de todo qu’on lui transmet. Il n’a pas d’autre état que celui d’afficher ses items ( this.props.items).

Le composant TodaApp lui, doit gérer des événements extérieurs et possède par conséquent différents états. A chaque fois que l’on va ajouter un item dans la todoliste, on va changer l’état du composant. Pour cela on utilisera les states. this.setState({}) mettra à jour les données propres au composant et déclenchera un nouveau rendu de lui même et de ses enfants si besoin.

C’est une nouvelle façon de penser , mais il en découle une logique descriptive, tout à fait adaptée à la problématique des interfaces. On retrouve également se genre de logique dans le framework Qt

Performances : du protoxyde d’azote dans ta timeline

Retour à la problématique de base. J’avais conçu un prototype ( avec AngularJs ) pour mon application afin de valider un concept sauf que au moment de faire tourner l’ensemble dans le pire cas d’utilisation ( environ 3500 éléments dynamiques ), grosse catastrophe :

6 secondes pour le premier rendu et une réactivité ingérable par la suite. Malgré quelques one-time bindings, la solution par le data-binding d’Angular n’est pas envisageable

 

application_react

Après une Reacriture du composant, en prenant en compte un temps d’apprentissage surprenamment court. On constate une nette amélioration de la vitesse d’affichage et de la réactivité mais dans le pire cas ce n’est toujours pas satisfaisant.

Cycle de vie d’un composant

La grande force de React est qu’il permet d’intervenir sur les différentes étapes de création de son composant et cela de manière assez fine. Par défaut, lorsque l’état d’un composant est mis à jour, il passe par le cycle de vie render , c’est à dire qu’il va exécuter la fonction de rendu qui lui est associée. Ce changement se transmet à ses composants fils.

Dans mon cas, j’ai un très gros tableau qui le plus souvent n’est réactualisé que sur certaines lignes sans modifier le reste. Il y a une option dans React qui permet d’utiliser ce genre de cas pour multiplier les performances en ne modifiant que les éléments devant l’être, il s’agit de shouldComponentUpdate  . En définissant précisément dans quel cas le composant doit être rendu, on augmente les performances de manière spectaculaire :

Avant optimisation

Avant optimisation

Après optimisation avec shouldComponentUpdate

Après optimisation avec shouldComponentUpdate

L’arme ultime ?

Vu comme ça on pourrait penser que React va être la solution à tous nos problèmes de performance, mais la réalité technique est toujours parsemée d’un « ça dépend » . Ce qui est plutôt intéressant, c’est qu’en préparant cet article, j’ai voulu créer un projet de test, et je suis tombé sur l’effet contraire à celui attendu : le rendu React peut être plus lent que celui d’AngularJs !

En creusant un peu, la solution se trouve sur le nombre de fonctions appelées en interne  :

Premier rendu de 5000 éléments avec AngularJs

Premier rendu de 5000 éléments avec AngularJs

Premier rendu de 5000 éléments avec ReactJs

Premier rendu de 5000 éléments avec ReactJs

Et il en va de même pour un changement sur un élément particulier, l’objet à rendre étant un tableau long mais relativement simple

Actualisation d'un élément sur 5000 par Angularjs

Actualisation d’un élément sur 5000 par Angularjs

Actualisation d'un élément sur 5000 par ReactJs

Actualisation d’un élément sur 5000 par ReactJs

Il y a bien plus d’appels en interne du coté de ReactJs, certainement lié à la création du virtual DOM. Je pensais m’etre trompé, mais je ne suis pas le seul à constater . Attention donc !

Angular vs React

Angular + React

On voit régulièrement passer ce genre de comparaison ou bien des questions du style « dois je choisir plutot Angular ou React  » ? Le fait est que c’est comme comparer une fenêtre à une maison. On peut changer les fenêtres mais ça ne règle pas le soucis de la charpente ! Si on considère AngularJs comme un framework MVC (MV*), alors React pourrait être une partie du V. Rien que la distinction entre librairie et framework doit mettre la puce à l’oreille. On pourra cependant adjoindre à React d’autres librairies comme Flux ( qui est plus un concept d’architecture qu’un réel framework) afin de tirer partie de la programmation réactive.

Au passage lorsqu’on choisit d’utiliser un framework, il faut le faire en toute conscience et pas seulement pour une fonctionnalité spécifique. Un framework c’est lourd, ça impose souvent une architecture, une dépendance non négligeable, alors gardez ça à l’esprit avant de choisir.

Angular propose lui aussi une vision sous forme de composants avec les directives, on peut donc facilement y inclure ReactJs et conserver son architecture applicative.

Au minimum on peut définir une directive de cette manière :


app.directive("exampleDirective", function() {
 return {
   template: <div></div>,
   link: function(scope, element) {

   }
 };
});

React ne nécessite qu’un point d’accroche au DOM pour y créer un composant. Dans une directive , on accède au DOM à travers l’argument element qui correspond au noeud encapsulé de la directive.

Il suffit alors d’appeler React.render sur l’élément brut .


React.render(<MonComposantReact data={scope.data} />, element[0]);

Voyez aussi comme il est facile de transmettre des variables du scope à notre composant par les propriétés.

 

Bien sur il est tout à fait possible de placer cette fonction de rendu dans un watch ou un évenement ce qui mettra à jour le composant si besoin :


scope.$watch(function(){return scope.data},function(){
    React.render(<MonComposantReact data={scope.data} />, element[0]);
 });

// ou sur un evenement  

$rootScope.$on("monEvent",function(){
    React.render(<MonComposantReact data={scope.data} />, element[0]);
});

Conclusion

J’espère que cet article vous aura permis d’élaguer un peu la question de ReactJs et notamment son utilisation avec AngularJs. C’est une librairie qui vaut vraiment le détour, non seulement pour la performance du virtual DOM face aux solutions classiques mais aussi pour son modèle de pensé sous forme de machine à état, tout à fait adapté à une interface utilisateur.

Le code peut paraître plus verbeux à première vue mais plus on progresse avec React plus il devient absolument logique et cohérent. La courbe d’apprentissage est très rapide, en une semaine vous avez fait le tour et touché aux fonctionnalités avancées. L’énorme avantage est que lorsqu’on vient d’écrire un composant même simple, on a exploré 70% de la librairie.

La documentation est clair et plutot complète. Les erreurs générées lors du dev sont exemplaires et proposent un véritable complément à la doc ( attention en version prod  elles ne sont plus du tout verbeuses, utilisez donc la version non minifiée pendant le développement  ) .

Je n’ai pas parlé de l’utilisation de ReactJs coté serveur mais sachez que cela résout tout à fait les problématiques de SEO que l’on rencontre avec AngularJs puisque le rendu de l’application est le même que l’on soit coté client ou coté serveur, c’est ce qu’on appel l’isomorphisme ( un exemple avec expressjs )

N’hésitez pas à réagir dans les commmentaires si vous avez déjà commencé à travailler avec React, que vous adorer ou au contraire détestez JSX ou tout simplement si vous avez des questions.

Notes:
1. générer une structure depuis une logique
2. introduire du comportement dans la structure
3. le fameux SoC est mis à mal
générer une structure depuis une logique
introduire du comportement dans la structure
le fameux SoC est mis à mal

18 Comments

    • Répondre maxdow |

      :) merci . J’espère avoir été le plus clair possible. Franchement c’est assez plaisant à utiliser une fois qu’on a compris. Il y a aussi le concept d’objets immuables que je n’ai pas vraiment abordé mais qui est important dans les states. On ne modifie pas un state on le set . Au passage, il y a les Immutability Helpers pour aider mais il manque encore un système pour mettre à jour sur un index variable d’un tableau ou d’un objet.

  • Répondre ami44 |

    Tu oublie que tu peux aussi générer le html sur le serveur avec react, puis passer en mode SPA apres. Regarde flux-example sur le github de yahoo.

    • Répondre maxdow |

      c’est vrai qu’il y a aussi cette utilisation mais je n’ai absolument pas testé l’approche coté serveur (pour l’instant ;) ) c’est pour cela que je n’en parle que brièvement dans la conclusion . Merci pour le retour :)

  • Répondre Jean-Baptiste Delhommeau |

    Merci beaucoup pour ce retour d’expérience ! Je ne suis pas polué par l’un ou l’autre étant donné que je ne connais pas angular. Mon application est de type Google drive. Je vais me laisser tenter par reactjs.

    • Répondre maxdow |

      Les deux feront surement l’affaire, n’oublie pas que React en soit n’est qu’une librairie pour gérer des composants. Tu peux faire ton architecture vanilla ou utiliser un système tel que Flux si tu utilise React ;)

  • Répondre Sylvain Pollet |

    J’ai exploré en long et en large le templating client et React semble être à l’opposé de toutes les bonnes pratiques que j’ai pu tirer de mon expérience: il met markup et logique au même endroit, il nécessite de recourir à un préprocesseur et il apporte une abstraction complète du DOM rendant quasi-impossible le débogage depuis les vues. Il va falloir un peu plus que des benchs pour me convaincre ! En attendant je continue à faire la même tête que cette jolie blonde.

    • Répondre maxdow |

      Ahhh j’aime bien ce genre de commentaire qui lancerait presque un long débat :) Honnêtement je suis d’accord avec ce que tu dis, je trouvais carrément ça sale au début et les bench , bah c’est cool mais souvent la perf, sauf besoin très précis on s’en fiche un peu d’avoir un affichage amélioré de 30ms. Quand j’ai découvert Angular j’ai été très séduit par le fait de rajouter un peu de puissance au HTML mais je me suis rapidement retrouvé dans la situation ou la logique « transpirait » dans la vue et ce n’est jamais bon. Enfait je crois que aucun système de template n’est réellement parfait…
      Pour le prepross, ça me gênait aussi , mais un workflow aujourd’hui est souvent poussé par un outil de build, alors vu que c’est 2 lignes et quelques ms à rajouter dans un gulpfile par exemple, finalement c’est transparent.
      Le debogage depuis les vues ? Il y a le plugin chrome react qui te permet d’explorer tes composants ainsi que les states associés. En fait le plus dur pour moi dans l’utilisation que j’ai pu faire de React ça a été de bien spécifier ce que je voulais que mon composant fasse précisément ! Tu as aussi quelques options pour auditer les Perf qui m’ont été utiles.
      Ensuite pour ce qui est d’un développement complet autour de React je n’ai pas essayé donc pas spécialement d’avis, mais dans le cas d’un composant graphique isolé ( ce qui est aussi le cas de ce retour d’expérience, je pense que ça peut peser dans la balance, en tout cas pour moi, la branche react va merger avec mon master ;)

      • Répondre Sylvain Pollet |

        C’est également mon ressenti, aucune lib de templating ne m’a totalement satisfait jusqu’ici. Elles ont toutes leurs avantages et inconvénients. J’avais comparé plusieurs approches dans cet article : http://sylvainpv.developpez.com/tutoriels/javascript/guide-templating-client/ ; depuis un an, je travaille sur ma propre lib de templating basés sur des principes radicalement différents de ceux adoptés par React, ce qui ne doit pas m’aider à l’apprécier ;) notamment des templates en pur HTML, sans interpolation de texte, avec un autoupdate à base de proxy au lieu de dirty-cheking.
        D’après moi, un bon templating est un lien entre markups et modèles préexistants, un lien le plus transparent et intuitif possible. Pour reprendre ton article, il ne s’agit pas de « générer une structure depuis une logique » ou l’inverse ; il s’agit de travailler sur les deux séparément, puis de les relier. Ça colle mieux avec ma façon de travailler, ainsi qu’à celle dans ma boîte où ce ne sont pas les mêmes équipes qui produisent les maquettes que ceux qui les dynamisent.

        • Répondre maxdow |

          Effectivement tu soulèves la question de la maintenance du code par des équipes de dev ou d’intégrateurs ce qui peut poser des problèmes avec des solutions comme React ou même AngularJs ( car le code de template peut vite devenir complexe ) . Ton idée est intéressante car elle n’engendre pas de compromis, les couches sont bien séparées et on branche une fois que tout est en place. Je ne pense pas de toute façon qu’il y ait de vérité à ce sujet. Il y a des solutions plus ou moins adaptés, suivant l’application, les équipes.. c’est comme souvent , cela reste un choix ;)

  • Répondre Benjamin Longearet |

    Hello! Article très sympa.
    Trop de gens essaye de faire des comparatifs XXX vs XXX au lieu d’essayer de combiner et de tirer le meilleur de chaque solution.
    Perso, je vois bien l’utilisation de React au sein de directive complexe et couteuse niveau UI sur nos projets pour éviter de polluer le digest cycle de l’appli et tirer au mieux les perfs React.

    • Répondre maxdow |

      Merci du retour. En effet c’est inutile d’essayer de comparer ce qui ne peut l’être. ( J’aime bien aussi la référence car/carpet pour java vs javascript ) . Pour ma part le mix AngularJs + React doit clairement être au sein d’une directive complexe comme tu le dis. Testé et approuvé, la $digestion n’en est que plus facile ;)

  • Répondre vincent euloge |

    Sympa cet article, j’y vois un peu plus claire concernant react.

    Je dev une grosse appli avec angular, et je suis confronté, moi aussi, a un problème sur les longues listes d’item, ou lorsque mon model est modifié a très haute fréquence.
    Donc si je comprend bien react a permis de diviser par deux le rendu de ton cas extrême … c’est toujours pas la panacé.
    Comment React sait quand re-render la vue lorsque le model change? j’imagine grâce à

    scope.$watch(function(){return scope.data},function(){
    React.render(, element[0]);
    });

    Du coup on se garde le plus gros boulet d’angular, le parcours de la liste des $watcher de toute l’appli a chaque scope.$apply.

    Peut être que pour profiter a 100% de react il faut se jeter dedans a 100% via Flux ?

    • Répondre maxdow |

      Enfait grâce à l’ajout de shouldComponentUpdate ce n’est pas par 2 mais par 10 ( ou plus peut être je n’ai pas mesuré précisément , l’expérience utilisateur est totalement fluide, ce qui compte au final plus que du Bench ).

      En ce qui me concerne, pour mon composant je réactualise la vue sur un événement ( $on ) , je n’ai donc pas le problème du gros $watch ( ce que j’essaye de limite au maximum ) . A mon avis, soit avec le $watch tu n’as pas d’impact sur les perfs et autant l’utiliser ( peut être à creuser du coté de $watchCollection plutot ? ) . Sinon oui repenser ton workflow, voir passer à un autre type d’architecture mais tu vas devoir tout casser l’existant, il y a surement plus simple ;)

      • Répondre vincent euloge |

        Ma solution depuis peu est d’écouter un événement broadcaster depuis un service, et c’est chaque directive concerné par un changement qui fait appel a son $digest local.
        Du coup les bases sont la pour remplacer cet appel a $digest par du react … c’est assez séduisant.

  • Répondre Hadrien Lanneau |

    Excellent article. Ce qui ne me plait pas dans ce que je lis de React, c’est que ça ressemble au canada dry du web component. Tant qu’à faire du web component, pourquoi ne pas directement utiliser les outils qui s’en rapprochent le plus comme Polymer ou Bosonic ?

    • Répondre maxdow |

      Merci :) En fait mis à part la question des components , ce que j’apprécie dans React c’est la logique qu’il apporte et la perf du VirtualDOM. Je n’ai pas encore testé le webcomponent « natif » à base de Bosonic ou Polymer mais je suis assez d’accord, c’est comme les directives d’AngularJs c’est mignon mais on sait bien que ça va finir dans une poubelle d’ici peu.

    • Répondre Hadrien Lanneau |

      Bon, j’ai essayé React, dans Angular. Ça marche. C’est indéniable. Ça s’intègre bien dans le workflow. Mais… je me pose juste des questions relatives à React en lui même. En écrivant du React, je n’ai pas l’impression d’obtenir un code plus clair qu’en l’écrivant en vanilla. C’est très verbeux et surtout, on perd le principal avantage qu’offre Angular : les templates HTLM que les intégrateurs peuvent toucher sans tout péter. Un autre défaut est qu’on ne peux pas utiliser de directives angular dans un composant React, du coup, on se retrouve à écrire un maximum de composants avec React.
      J’ai déjà commencé à écrire certaines directives sans utiliser la mécanique d’Angular pour gagner en perfs, et ça marche bien. Penses tu qu’il y a vraiment un interêt à utiliser React ?

So, what do you think ?