Accueil / Articles

D3.js au sein de React : liaisons de données, SVG et la règle de propriété useEffect

Apprenez ce qu’est réellement D3, comment fonctionnent les liaisons d’entrée/mise à jour/sortie, pourquoi SVG est adapté, et comment diviser le contrôle DOM afin que D3 et React collaborent dans un graphique en barres.

Éditeur : REACTAPP.TOP7 min de lecture1530 mots

Sources: Harnessing the Power of Data Visualization: Integrating D3.js with React

Trois choses à savoir en premier

  1. D3 est un outil, pas une bibliothèque de graphiques. Ceux qui s’attendent à quelque chose comme Chart.js se heurtent à des difficultés. D3 est un outil de bas niveau permettant de lier des données aux éléments DOM, ce qui explique à la fois sa puissance et sa courbe d’apprentissage plus raide.
  2. React et D3 se disputent les mêmes nœuds DOM. La solution est simple : React affiche le conteneur SVG, tandis que D3 gère tout ce qui s’y trouve à l’aide d’un useEffect. En ignorant cette règle, vous passerez des heures à résoudre des conflits étranges.
  3. Le schéma enter, update et exit semble étrange au début. Après quelques utilisations, il devient intuitif, et d’autres approches pour les graphiques pilotés par des données semblent alors maladroites.

Qu’est-ce que D3 vraiment ?

Ce nom signifie Documents pilotés par des données. Le mot à retenir est « documents » : des éléments DOM dont l’apparence est déterminée par des données. D3 lie des tableaux à ces éléments et vous permet de définir leur apparence en fonction des valeurs contenus dans ces tableaux. Un graphique n’est qu’un résultat possible, et la bibliothèque elle-même ne connaît pas de types de graphiques.

C’est là que résident à la fois la force et la difficulté de cette approche. D3 ne prend aucune décision à votre place. Chaque propriété visuelle, chaque marqueur d’axe, chaque couleur et chaque interaction doit être spécifiée par vous. Vous bénéficiez d’une liberté créative totale, mais cela implique aussi beaucoup de travail de saisie.

Lien des données et le DOM

C’est là que la plupart des débutants abandonnent, il est donc utile d’aborder ce sujet progressivement.

Dans le JavaScript classique, on crée un élément, on le remplit de contenu et on l’insère dans la page. Avec D3, on sélectionne un groupe d’éléments, y compris ceux qui n’existent pas encore, on lie un tableau à cette sélection, puis on reçoit trois groupes en retour :

  • les éléments qui existent déjà et qui ont simplement besoin de données mises à jour (la sélection d’actualisation),
  • les points de données qui n’ont pas encore d’élément (la sélection d’insertion),
  • les éléments dont les données ont disparu (la sélection de sortie).

Le point étrange, c’est d’appeler des méthodes sur une sélection vide. Vous sélectionnez tous les éléments rect à l’intérieur d’un SVG qui n’en contient aucun, vous appelez .data(myArray), puis .enter(), puis .append('rect'), et des rectangles apparaissent. Ce qui se passe, c’est que .enter() génère un placeholder pour chaque donnée manquant d’élément, et .append() transforme chaque placeholder en un vrai nœud. À interpréter comme « pour chaque point de données sans élément, en créer un ».

Avant que cela ne fonctionne, le code D3 ressemble à de la magie. Après, il devient facile à comprendre.

Définir les propriétés visuelles à partir des données

Une fois les données liées, l’étape suivante est plus facile à saisir. Tout attribut ou style peut être calculé à partir de la donnée :

selection.attr('height', d => yScale(d.value))

Ici, d représente les données associées à cet élément en particulier, tandis que yScale est une fonction qui convertit l’intervalle de valeurs en pixels. La hauteur de la barre dépend directement de ses données. Modifiez les données, exécutez à nouveau la commande, et la hauteur s’adaptera automatiquement. Presque tout dans une visualisation D3 repose sur des variations de ce principe : assigner une propriété visuelle en fonction de la valeur correspondante.

Pourquoi SVG ?

D3 dessine à l’intérieur d’SVG, et les raisons vont au-delà de simples considérations historiques.

SVG étant basé sur des vecteurs, les formes y sont définies mathématiquement plutôt que comme des grilles de pixels. Un graphique en barres net et précis sur un ordinateur portable l’est tout autant sur un écran 4K, sans aucun effort supplémentaire, et les exports ne deviennent pas flous à des résolutions inadaptées.

Pour ce qui est de l’interactivité, les formes SVG sont des éléments DOM ordinaires qui participent au système d’événements du navigateur tout comme un bouton. Les actions de survol, de clic et de toucher fonctionnent sans nécessiter de test de collision personnalisé, car il suffit d’attacher des écouteurs.

Le coût devient problématique à grande échelle. Des milliers de nœuds SVG ralentissent la page, donc représenter 50 000 points distincts en éléments séparés est préjudiciable. Le rendu Canvas convient mieux à ce type de situation. Pour les tableaux de bord, les graphiques commerciaux et la plupart des outils analytiques, SVG est suffisant et son intégration avec les événements en vaut la peine.

Interactivité et flexibilité

Les interfaces d’exploration de données, où les utilisateurs analysent, filtrent et examinent les données sous différents angles, sont le domaine où D3 surpasse les outils de graphiques statiques. Vous contrôlez chaque événement et chaque transition.

La plupart des bibliothèques de graphiques vous fournissent des types de graphiques à configurer : barres, lignes, cercles, avec des options pour les couleurs, les étiquettes et les légendes. D3, quant à lui, fournit des éléments de base : échelles, axes, générateurs de trajectoires et algorithmes de mise en page. Un type de graphique n’est rien d’autre qu’une combinaison de ces éléments, il n’y a donc pas de liste des fonctionnalités prises en charge. Si une visualisation n’existe nulle part, vous pouvez la créer, limité uniquement par SVG et les mathématiques.

Cela implique un travail supplémentaire. Le fait que cela en vaille la peine dépend du projet. Pour les graphiques commerciaux standard, Recharts ou Chart.js suffisent, et vous pouvez passer à autre chose. Pour des graphiques interactifs personnalisés où la présentation est cruciale, D3 est le choix le plus adapté.

Les fonctions que vous utiliserez constamment

Il ne s’agit pas d’une référence complète, seulement des appels qui apparaissent dans le travail quotidien :

  • d3.select() sélectionne le premier élément correspondant à un sélecteur. Dans React, d3.select(svgRef.current) est la méthode habituelle pour obtenir le conteneur.
  • d3.selectAll() renvoie toutes les correspondances. Le liage de données commence généralement ici car plusieurs éléments sont impliqués.
  • selection.data() associe chaque élément de l’array à un élément concret, afin que d soit disponible lors des appels ultérieurs. Tout le reste dépend de cette étape.
  • selection.enter() crée une sélection de remplacement pour les données qui n’ont pas de nœud, et .append() crée ensuite les nœuds correspondants.
  • selection.attr() définit des attributs, comme la ligne de hauteur montrée ci-dessus, que vous écrirez des centaines de fois.
  • selection.style() fait de même pour le CSS, comme la couleur de remplissage ou l’opacité qui dépend du fait qu’un élément soit actif.
  • selection.transition() anime les changements. Placez-le avant .attr() ou .style() afin que les valeurs s’interpolent en douceur, transformant ainsi un saut en animation.
  • d3.scaleLinear() convertit des valeurs d’une plage numérique à une autre. Avec un domaine de 0 à 100 et une plage de 0 à 400, une valeur de 50 correspond au pixel 200. Les échelles apparaissent dans presque toutes les visualisations.
  • d3.axisBottom() et d3.axisLeft() génèrent des axes avec des graduations et des étiquettes issues d’une échelle, vous épargnant ainsi la nécessité de dessiner manuellement les marques.
  • d3.max() renvoie la plus grande valeur, souvent utilisée pour définir les domaines des échelles, comme dans d3.max(data, d => d.value).
  • d3.transition() coordonne les animations sur plusieurs éléments en même temps.
  • Utiliser D3 à l’intérieur de React

    Le problème que personne ne mentionne avant de le rencontrer : React gère un DOM virtuel et applique ses propres modifications au DOM réel, tandis que D3 modifie directement le DOM réel. Lorsque les deux bibliothèques opèrent sur le même élément, la réaffichage de React efface le travail effectué par D3, ou D3 insère des nœuds que React ne connaît pas, ce qui perturbe le prochain affichage.

    La solution consiste à diviser les responsabilités. React affiche le conteneur SVG sans toucher à son contenu. D3 accède à ce conteneur via un ref et effectue tout son travail à l’intérieur d’un useEffect. Lorsque les données changent, une mise à jour d’état déclenche cet effet, et D3 efface puis redessine le contenu. Aucune des deux bibliothèques ne interfère avec l’autre.

    1. Installer les paquets

    Ajoutez D3 aux côtés de React et ReactDOM à l’aide de votre gestionnaire de paquets.

    npm install d3 react react-dom
    

    Les utilisateurs de Yarn font de même avec leur propre commande add.

    yarn add d3 react react-dom
    

    2. Écrire le composant

    Le côté React a une seule tâche : afficher un élément SVG et fournir à D3 un référent vers celui-ci. Rien d’autre dans la visualisation ne relève de React.

    3. Dessiner avec useEffect

    Dans l’effet, écrivez du code D3 exactement comme vous le feriez en dehors de React, en utilisant le référent pour trouver le conteneur. Deux échelles effectuent les calculs de coordonnées : l’une mappe les étiquettes de catégorie aux positions horizontales, et l’autre mappe les valeurs des données aux positions verticales. La hauteur de chaque barre correspond au bord inférieur du graphique moins la valeur échelonnée. Cette partie pose problème car l’axe Y de l’SVG pointe vers le bas, ce qui fait que des barres plus hautes correspondent à des valeurs Y plus petites.

    4. Afficher le composant

    Placez le composant dans votre application et transmettez-lui un tableau de données. Modifiez le tableau et le graphique se redessine : React détecte la modification de la propriété, l’effet est déclenché, et D3 efface puis redessine le graphique. Le lien entre l’état de l’application et la visualisation est automatique.

    5. Lancez l’application

    Exécutez le serveur de développement avec npm.

     npm start
    

    Ou avec Yarn.

     yarn start
    

    Vous verrez un graphique en barres dans le navigateur. À partir de là, tout suit le même schéma : des informations contextuelles en survol, des échelles de couleurs, des mises à jour animées, un zoom et d’autres types de graphiques. Tout cela fait partie de cet effet et utilise les fonctions mentionnées ci-dessus.

    En résumé

    D3 et React collaborent une fois que la structure est correcte. React gère le conteneur et le cycle de vie des composants, tandis que D3 s’occupe du dessin à l’intérieur. Gardez cette séparation claire afin que chaque bibliothèque fasse ce pour quoi elle a été conçue.

    La récompense est la capacité à créer des visualisations que aucun package de graphiques ne propose, car vous travaillez avec des primitives et non avec des options de configuration. Lorsque le graphique lui-même fait partie de la valeur du produit, cela est important. Lorsqu’un tableau de bord a simplement besoin d’un graphique linéaire, utilisez Recharts et évitez les complications supplémentaires.