Aplicaciones interactivas con React y D3

Si está pensando en visualizar datos en React, eche un vistazo a Semiotic, un marco de visualización de datos que desarrollé para React.

Reunir a D3.js y React es una de esas cosas que no es nueva pero que aún no está lo suficientemente establecida como para señalar una forma segura de hacerlo. En este extracto de mi libro D3.js in Action, Second Edition, te mostraré las técnicas principales para combinar React y D3 y explicaré sus fortalezas y debilidades. El código para este ejemplo se puede encontrar en el código del capítulo Reaccionar en D3.js en el repositorio de Action.

El ejemplo completo que compila en el Capítulo 9 de D3.js en Acción. En este artículo lograremos obtener parte del gráfico de barras y parte del mapa.

Comenzamos con un diseño para nuestro tablero. Los diseños pueden ser bocetos o conjuntos detallados de requisitos del usuario. Imaginemos que trabaja para el principal vendedor en línea europeo de manteles, MatFlicks, y que está a cargo de crear un tablero que muestre su implementación en Norteamérica y Sudamérica. El genial CEO de MatFlicks, Matt Flick, decidió que la estrategia de implementación sería alfabética, por lo que Argentina tiene acceso el Día 0 y cada día un país más tiene acceso al increíble inventario de MatFlicks. Necesitan ver cómo progresa el despliegue geográficamente, a lo largo del tiempo y en total por país. La siguiente figura muestra un boceto simple para lograr esto usando varios de los gráficos que exploro a lo largo de D3.js en acción. Vamos a generar aleatoriamente datos de MatFlicks, y cada país solo generará datos aleatorios en miles de millones de dólares de ingresos por día después de su implementación.

Con un panel de datos como este, queremos proporcionar al usuario múltiples perspectivas sobre los datos, así como la capacidad de profundizar en los datos y ver puntos de datos individuales. Utilizaremos un gráfico de líneas para ver el cambio a lo largo del tiempo, un gráfico de barras para los cambios totales sin procesar y un mapa para que los usuarios puedan ver la distribución geográfica de nuestros datos. También queremos permitir que los usuarios corten y corten sus datos, funcionalidad que se logra fácilmente con un pincel.

Un boceto de un tablero que muestra un mapa, un gráfico de barras y un gráfico de área apilada que muestra nuestros datos.

A partir del boceto, puede imaginar fácilmente las posibilidades de interacción y los cambios que desee ver en función de la actividad del usuario; por ejemplo, resaltar qué elementos en cada gráfico corresponden a elementos en otros gráficos, o dar más detalles sobre un elemento en particular basado en un clic. Ese tipo de filtrado dinámico e interactividad se explora en el capítulo completo. El CSS para el panel no es mucho para este ejemplo, solo el relleno y el trazo para el mapa que haremos. Como con la mayoría de las visualizaciones de datos, gran parte del estilo estará en línea.

Panel de control CSS
path.countries {
   ancho de carrera: 1;
   carrera: # 75739F;
   relleno: # 5EAFC6;
}

Comenzando con React

React es un sistema de gestión del ciclo de vida de la vista que forma parte de un marco de aplicación y un patrón de desarrollo muy populares. React es la capa de vista y le permite definir componentes HTML con un comportamiento personalizado, lo cual es muy útil para componer aplicaciones. Utiliza un lenguaje JavaScript + HTML llamado JSX que me disgustó cuando lo vi por primera vez, pero ahora me encanta. No me gustó porque siempre sentí que JavaScript y HTML deberían vivir en mundos totalmente separados, pero descubrí más tarde que escribir HTML dentro de JavaScript podría ser increíblemente útil cuando estás manipulando el DOM como hemos estado con Vanilla D3 en todo momento este libro.

Por lo general, cuando ve ejemplos de React, lo emparejan con un tipo de sistema de administración de estado como Flux o Redux. No haremos eso en este capítulo. Este es un solo capítulo, y puedes encontrar libros completos sobre React.

Necesitará el nodo y el administrador de paquetes de nodo (npm) instalados en su sistema, así como un poco de comodidad con la línea de comando. Hay excelentes libros sobre React, como React Quickly, por lo que esto solo rayará la superficie, pero solo este capítulo lo llevará al punto de una aplicación de visualización de datos React completamente autónoma.

¿Por qué reaccionar, por qué no X?

Obviamente, React es la mejor biblioteca que se haya creado y, si te gusta Angular, eres tonto, hermano (y ni siquiera me haces empezar con Ember). No en realidad no. Eso es horrible, y es una pena que la gente se involucre tanto en la justicia de su biblioteca en particular.

En realidad, solo quería mostrarle a la gente cómo lidiar con D3 en un entorno moderno similar a MVC y sé que reacciona mejor. Incluso si nunca usa React, probablemente verá patrones en este capítulo que se aplican a otros marcos. E incluso si odia los marcos de aplicaciones, puede usar la mayor parte del código en este capítulo en su propio panel personalizado, enrollado a mano, bellamente opaco.

Fundamentalmente, React consiste en un marco de creación de componentes que le permite construir elementos autónomos (como div o svg: rect) que tienen métodos de representación personalizados, propiedades, estado y métodos de ciclo de vida.

Hacer

Una de las principales características de React es que realiza un seguimiento de una copia del DOM, conocido como DOM virtual, que puede usar para representar solo elementos que deben cambiar en función de la recepción de nuevos ciclos de almacenamiento de datos y la aceleración de sus aplicaciones web . Este fue el gran punto de venta de React cuando cayó por primera vez, pero se ha vuelto popular con otros sistemas de representación de vistas. La función render () en cada componente React devuelve los elementos que será creado por React (típicamente descrito usando JSX, que se presenta en este capítulo).

Accesorios

Los atributos de un componente se envían cuando se crea y se conocen como accesorios. Estos accesorios de un componente React están típicamente disponibles en las funciones del componente a través del contexto this como this.props. En ciertos casos, como componentes o constructores sin estado, no lo usará para acceder a ellos, pero no lo haremos en este capítulo, por lo que necesitará un libro dedicado a React para conocer los otros patrones. Esta estructura le permite enviar datos desde componentes primarios a componentes secundarios y puede usar esos datos para modificar cómo se representa el componente. Verá esto en detalle cuando entremos en el código.

Estado

Mientras que los accesorios se envían a un componente, el estado de un componente se almacena y modifica internamente dentro del componente. Al igual que this.props, hay un this.state correspondiente que le dará el estado actual. Cuando modifica el estado (usando this.setState en un componente), se activará automáticamente una nueva representación a menos que haya modificado shouldComponentUpdate (un método de ciclo de vida que se trata en la siguiente sección).

Métodos de ciclo de vida

Los componentes de reacción exponen métodos de ciclo de vida que se activan cuando el componente se crea y actualiza y recibe sus accesorios. Son increíblemente útiles e incluso necesarios en ciertos casos de uso, como veremos más adelante. Usted tiene, por ejemplo, shouldComponentUpdate, que le permite especificar la lógica de si el componente se vuelve a representar cuando recibe nuevos accesorios o estado. También hay willComponentUpdate y didComponentUpdate para agregar funcionalidad a su componente antes o después de que se actualice, junto con métodos similares para cuando el componente se monta o sale por primera vez (y algunos más). Entraré en estos métodos a medida que se apliquen a nuestras necesidades de visualización de datos, pero no los mencionaré a todos.

react-create-app: configurando su aplicación

Uno de los desafíos del desarrollo moderno es configurar su entorno. Afortunadamente, hay una herramienta de línea de comandos que te ayuda a comenzar y está respaldada por el equipo React: create-react-app

En OSX puede abrir su ventana de terminal y ejecutar los siguientes comandos:

npm install -g create-react-app
crear-reaccionar-aplicación d3ia
cd d3ia /
npm start

Configurar su aplicación React es así de fácil. Si navegas a localhost: 3000 verás la página de creación de la aplicación repetitiva a continuación. Si tiene algún problema o necesita instrucciones para Windows, consulte https://github.com/facebookincubator/create-react-app.

La página predeterminada con la que se crea create-react-app.

Además de iniciar su servidor de nodo ejecutando el código, esto creará toda la estructura que necesita para crear e implementar una aplicación React que vamos a usar para construir nuestro panel de control. Esa estructura contiene un archivo package.json que hace referencia a todos los módulos incluidos en su proyecto y al que necesitamos agregar un par de módulos más para hacer nuestro tablero. Agregamos módulos usando NPM y, aunque podríamos incluir toda la biblioteca D3 y seguir codificando como lo hemos hecho, es mejor que instale los módulos individuales y comprenda cómo funciona la importación de esos módulos. En el directorio de su proyecto, ejecute lo siguiente para instalar el módulo d3-scale:

npm i –SE d3-scale

Este comando (npm i es la abreviatura de npm install) instala la última versión de d3-scale (que nos da acceso a todas esas maravillosas escalas que hemos estado usando en los últimos ocho capítulos) y la etiqueta –SE guarda la versión exacta en su package.json para que cuando desee implementar esta aplicación en otro lugar se instale d3-scale. Junto con d3-scale, haga lo mismo con los siguientes módulos:

forma d3
d3-svg-legend
d3-array
d3-geo
selección d3
transición d3
cepillo d3
eje d3

Al instalar módulos de forma individual como esta, reduce la cantidad de código que implementará con su aplicación, disminuyendo el tiempo de carga y mejorando la capacidad de mantenimiento.

JSX

JSX se refiere a JavaScript + XML, un lenguaje de codificación integrado de JavaScript y HTML que le permite escribir HTML en línea con su código JavaScript. Requiere que el código se transpile a JavaScript simple (su navegador no puede ejecutar JSX de forma nativa), pero siempre que tenga su configuración de transpilación (que react-create-app ya hace por nosotros) puede escribir código como este:

const data = ['uno', 'dos', 'tres']
const divs = data.map ((d, i) => 
{d}
)
const wrap = 
className = 'wrapper'> {divs} 

Y puede crear una matriz de tres elementos div, cada uno de los cuales tendrá la cadena correspondiente de su matriz como contenido. Observe algunas cosas que suceden aquí. Uno, cuando comenzamos a escribir en HTML, tenemos que usar llaves (en negrita para enfatizar más arriba) para salir de él si queremos poner js allí. Si no hubiera puesto llaves alrededor de la d, por ejemplo, entonces todos mis divs habrían tenido la letra "d" como su contenido. Otro es que el estilo es un objeto pasado a un elemento y ese objeto necesita claves CSS que generalmente son mayúsculas y minúsculas (como margin-left) convertidas en camelcase (marginLeft). Cuando estamos creando una serie de elementos, cada uno necesita una propiedad "clave" que le otorgue una clave única (como la clave opcional cuando usamos .data () con D3). Finalmente, cuando desea establecer la clase CSS de un elemento, debe usar className, porque la clase está reservada.

Hay más en JSX, pero eso debería ser suficiente para que pueda entender el código que va a ver. Cuando vi JSX por primera vez, estaba convencido de que era una idea horrible y planeé usar solo las funciones de representación de JavaScript puro que tiene React (no es necesario usar JSX para usar React), pero después de un par de semanas, caí en la trampa. amar con eso La capacidad de crear elementos sobre la marcha a partir de datos realmente me atrajo debido a mi experiencia con D3.

Representación D3 tradicional con React

El desafío de integrar D3 con React es que React y D3 quieren controlar el DOM. Todo el patrón de selección / entrada / salida / actualización con D3 está en conflicto directo con React y su DOM virtual. Si vienes a Reaccionar desde D3, renunciar a controlar el DOM es uno de esos momentos de "manos frías y muertas". La forma en que la mayoría de las personas usa D3 con React es usar React para construir la estructura de la aplicación y representar elementos HTML tradicionales, y luego, cuando se trata de la sección de visualización de datos, pasan un contenedor DOM (generalmente un ) a D3 y use D3 para crear y destruir y actualizar elementos. En cierto modo, es similar a la forma en que solíamos usar applets de Java o Flash para ejecutar un cuadro negro en su página mientras que el resto de su página se representa por separado. El beneficio de este método de integración de React y D3 es que puede usar el mismo tipo de código que ve en todos los ejemplos principales de D3. La dificultad principal es que necesita crear funciones en varios eventos del ciclo de vida de React para asegurarse de que su viz se actualice.

La lista a continuación muestra un componente de gráfico de barras simple creado con este método. Cree este componente en su directorio src / y guárdelo como BarChart.js. En React, los nombres de archivos de componentes y los nombres de funciones generalmente se diferencian de otros archivos de código y funciones utilizando camelcase y escribiendo en mayúscula la primera letra como esta.

BarChart.js

import React, {Component} desde 'react'
importar './App.css'
importar {scaleLinear} desde 'd3-scale'
importar {max} desde 'd3-array'
importar {select} desde 'd3-selection'
class BarChart extiende el componente {
   constructor (accesorios) {
      super (accesorios)
      this.createBarChart = this.createBarChart.bind (this)
   }
   componentDidMount () {
      this.createBarChart ()
   }
   componentDidUpdate () {
      this.createBarChart ()
   }
   createBarChart () {
      const node = this.node
      const dataMax = max (this.props.data)
      const yScale = scaleLinear ()
         .dominio ([0, dataMax])
         .range ([0, this.props.size [1]])
   seleccionar (nodo)
      .selectAll ('rect')
      .data (this.props.data)
      .entrar()
      .append ('rect')
   
   seleccionar (nodo)
      .selectAll ('rect')
      .data (this.props.data)
      .salida()
      .retirar()
   
   seleccionar (nodo)
      .selectAll ('rect')
      .data (this.props.data)
      .style ('relleno', '# fe9922')
      .attr ('x', (d, i) => i * 25)
      .attr ('y', d => this.props.size [1] - yScale (d))
      .attr ('altura', d => yScale (d))
      .attr ('ancho', 25)
   }
render () {
      return  this.node = node}
      ancho = {500} altura = {500}>
      
   }
}
exportar gráfico de barras predeterminado

Algunas explicaciones sobre lo que está sucediendo en el código aquí:

  • Debido a que estamos importando funciones D3 desde módulos, no tienen el d3. prefijo, en cambio son la función importada como scaleLinear.
  • En el constructor, debe vincular el componente como contexto a cualquier función interna nueva si desea acceder a this.props o this.state en esa función (esto no necesita hacerse para ninguna función del ciclo de vida existente).
  • Al ejecutar this.createBarChart en componentDidMount y componentDidUpdate, cada vez que el componente se monta o recibe nuevos accesorios / estado, se activa la función de gráfico de barras.
  • this.node se establece en la propiedad ref del elemento svg y actúa como una referencia al nodo DOM real generado por React, por lo que puede transferir ese nodo DOM a su funcionalidad D3.
  • el tamaño y los datos se transmiten como accesorios al componente, por lo que puede acceder a ellos con this.props.size y this.props.data a su código D3.
  • Render solo devuelve un elemento SVG que espera su código D3. A continuación, veremos cómo usar React para generar el gráfico completo.

Hacer estos cambios y guardarlos no mostrará ningún efecto inmediato porque no está importando y procesando este componente en App.js, que es el componente que su aplicación procesó inicialmente. Cambie App.js para que coincida con la siguiente lista.

Hacer referencia a BarChart.js en App.js
import React, {Component} desde 'react'
importar './App.css'
importar BarChart desde './BarChart'
La aplicación de clase amplía el componente {
   render () {
   regreso (
      
      
      

panel de control de d3ia

      
      
             
      
   )    } }
Exportar aplicación predeterminada

Los cambios aquí son que estamos importando nuestro componente recién creado (BarChart.js) y estamos pasando algunos datos y tamaño a ese componente (así es como estamos accediendo a él en props.data y props.size en createBarChart) .

Cuando guarde App.js con estos cambios, verá algo bastante bueno si tiene su servidor funcionando: actualiza automáticamente la página para mostrarle lo que se muestra en la figura a continuación. Es Webpack, el paquete de módulos incluido en create-react-app, que actualiza automáticamente su aplicación en función de los cambios en su código.

Su primera aplicación React + D3, con un gráfico de barras simple representado en su aplicación.

Ya puede imaginar mejoras como escalar las barras para que se ajusten al ancho, que veremos más adelante. Pero por ahora pasemos al otro método de visualización de datos usando D3 y React.

Reaccione para la creación de elementos, D3 como el núcleo de visualización

En lugar de usar ref para obtener el nodo DOM real y pasar ese nodo DOM a D3, puede usar D3 para generar todas las instrucciones de dibujo necesarias y usar React para crear los elementos DOM reales. Hay desafíos con este enfoque en la creación de transiciones animadas y elementos arrastrables, pero de lo contrario es preferible porque creará código que será más fácil de mantener por sus colegas menos inclinados hacia D3.

El siguiente código muestra cómo podemos hacer esto para recrear uno de los mapas realizados en el capítulo de visualización de datos geoespaciales de D3.js en acción. En este ejemplo, importamos datos geográficos (en este caso world.js) en lugar de cargar geodatos como muestran los ejemplos tradicionales. Eso se hace transformando el archivo geojson .js agregando una pequeña sintaxis de exportación ES2015 al comienzo del objeto JSON (puede ver el código en el repositorio de github). El código en la siguiente lista es similar a lo que hemos visto antes, excepto que ahora lo estamos usando para crear elementos JSX para representar a cada país y estamos incluyendo los datos geográficos en lugar de usar una solicitud XHR.

WorldMap.js
import React, {Component} desde 'react'
importar './App.css'
Importar datos mundiales desde './world'
importar {geoMercator, geoPath} desde 'd3-geo'
clase WorldMap extiende Componente {
   render () {
      proyección const = geoMercator ()
      const pathGenerator = geoPath (). proyección (proyección)
      países const = worlddata.features
         .map ((d, i) => )
   return 
   {países}
   
   }
}
exportar WorldMap predeterminado

En lugar de jugar con llamadas asíncronas (usando d3.json, por ejemplo), podemos importar los datos del mapa, ya que no cambiará. Creo que es mejor transformar cualquier activo estático en .js e importarlo en lugar de usar solicitudes XHR. Usamos Array.map nativo para mapear las matrices a svg: elementos de ruta y llenar el atributo d de cada uno de esos elementos de ruta utilizando la funcionalidad geo de D3. Ese conjunto de rutas simplemente se coloca en algunos corchetes dentro del svg y eso es todo para crear un mapa coroplético en Reaccionar con D3. Es casi exactamente lo mismo que el patrón de enlace de datos que vemos en D3, excepto que usamos Array.map nativo para mapear los elementos de datos individuales a elementos DOM debido a la magia de JSX.

Un mapa básico representado a través de React y JSX con D3 que proporciona las instrucciones de dibujo.

En mi propia práctica, prefiero usar este método, porque encuentro los eventos del ciclo de vida en React, así como la forma en que crea, actualiza y destruye elementos para que sean más completos que tratarlo a través de D3. Y a medida que React y otros marcos similares maduran, los problemas con el desarrollo de la interactividad y la animación se vuelven cada vez menos difíciles.

En el capítulo 9 de D3.js en acción, este ejemplo continúa con la adición de un diagrama de flujo, un pincel, un tamaño receptivo y más. Pero esto le muestra las dos formas principales de abordar la integración de D3 y React.