En este artículo, Ilya Zayats le mostrará que, desde la perspectiva de React, no hay ninguna diferencia en qué renderizar. React ayuda a organizar una aplicación en partes pequeñas y digeribles para los humanos. Puede evitar interacciones internas complejas entre componentes pequeños, mientras su aplicación continúa siendo increíblemente rápida debido a la diferenciación DOM que React realiza bajo el capó. Tratar de comprender qué hay de malo en un gráfico o una visualización simplemente mirando las plantillas del generador SVG suele ser abrumador, y los intentos de mantener la estructura interna o la separación de preocupaciones suelen ser complejos y tediosos. Entonces, ¿podemos aplicar las mismas técnicas a los gráficos web, en particular a SVG? ¡Sí!
React es una de las formas más populares hoy en día de crear una interfaz de usuario basada en componentes. Ayuda a organizar una aplicación en partes pequeñas y digeribles para los humanos. Con su enfoque de "volver a renderizar el mundo entero", puede evitar interacciones internas complejas entre componentes pequeños , mientras su aplicación continúa siendo increíblemente rápida debido a la diferenciación DOM que React hace bajo el capó (es decir, actualiza solo las partes de el DOM que necesita ser actualizado). Pero, ¿podemos aplicar las mismas técnicas a los gráficos web, en particular a SVG? ¡Sí!
No sé ustedes, pero para mí el código SVG se vuelve complicado bastante rápido. Tratar de comprender qué hay de malo en un gráfico o una visualización simplemente mirando las plantillas del generador SVG (o la fuente SVG misma) suele ser abrumador, y los intentos de mantener la estructura interna o la separación de preocupaciones suelen ser complejos y tediosos.
Gracias a Facebook, tenemos React para hacer el trabajo por nosotros.
Primero, React funciona con DOM (y DOM no es solo HTML). Por lo tanto, puede trabajar con SVG exactamente como lo hace normalmente con HTML. Por ejemplo, aquí hay un círculo:
import React from 'react';export default class App extends React.Component { render() { return ( svg circle cx={50} cy={50} r={10} fill="red" / /svg ) }}
Como dije, desde la perspectiva de React, no hay diferencia entre trabajar con HTML o trabajar con SVG (o, como habrás escuchado últimamente, vistas móviles o lienzos).
Pero intentemos crear algo un poco más complejo, para que podamos ver cómo React ayuda a estructurar SVG de una manera comprensible para los humanos .
Imagine que necesitamos crear un panel para visualizar el conjunto de datos más complejo jamás creado:
[ [1, 3], [2, 5], [3, 2], [4, 16], [18, 5]]
Esto es sólo una matriz de pares de coordenadas xey, nada más.
Usaré React Hot Boilerplate como punto de partida para ahorrar tiempo en la configuración de nuestros elementos esenciales de desarrollo, incluidos los siguientes:
- webpack Este paquete de módulos muy potente procesará y gestionará todas las dependencias por nosotros.
- babel Este transpilador de código nos permite usar ECMAScript 6 (ES6) en navegadores que aún no lo soportan.
- reaccionar-hot-loader Esta gran herramienta actualizará nuestros componentes de React en el navegador sin recargar toda la página.
Comenzaremos cambiando script/index.js
para iniciar nuestro panel:
import React from 'react';import ReactDOM from 'react-dom';import App from './app';import data from './data';ReactDOM.render(App data={data} /, document.getElementById('root'));
Aquí script/data.js
está solo nuestra matriz de datos que se mencionó anteriormente:
export default [ [1, 3], [2, 5], [3, 2], [4, 16], [18, 5] ];
Ahora, prepararemos nuestro script/app.js
para representar nuestro gráfico futuro:
import React from 'react';import Graph from './components/graph';export default class App extends React.Component { render() { return ( Graph data={this.props.data} / ) }}
Esta es la parte más interesante: la oportunidad de detenernos a pensar en qué consiste nuestra gráfica. Este es uno de los mejores procesos al desarrollar con React: podemos pensar primero en componentes de alto nivel y luego dividirlos en otros más granulares.
Por ejemplo, scripts/components/graph.js
:
import React from 'react';import Axis from './axis';import GraphBody from './graph_body';export default class Graph extends React.Component { render() { return ( svg Axis length={width} horizontal={true} / Axis length={height} horizontal={false} / GraphBody data={this.props.data} / /svg ) }}
Dos ejes y un cuerpo gráfico me parecen lógicos. Por supuesto, el código no funcionará. Este es solo un intento de darle forma a una API inicial de nuestro gráfico: aún no hemos implementado componentes secundarios y tenemos algunas variables no definidas como width
y height
. Terminemos esto paso a paso.
Necesitamos establecer algunas dimensiones para nuestro gráfico. Podríamos codificarlos, pero es mejor usarlos defaultProps
:
export default class Graph extends React.Component { static defaultProps = { width: 800, height: 600 };
Ahora, si pasamos no width
o height
al Graph
componente como props
, se utilizarán los valores predeterminados.
Podríamos transferir estos valores al svg
mismo:
svg width={this.props.width} height={this.props.height}
Y luego podríamos ampliar las declaraciones de los ejes y el cuerpo del gráfico dándoles algunas posiciones iniciales:
import React from 'react';import Axis from './axis';import GraphBody from './graph_body';export default class Graph extends React.Component { static defaultProps = { width: 800, height: 600 }; render() { return ( svg width={this.props.width} height={this.props.height} Axis x={20} y={this.props.height - 100} length={this.props.width} horizontal={true} / Axis x={20} y={0} length={this.props.height - 100} horizontal={false} / GraphBody x={20} y={this.props.height - 100} data={this.props.data} / /svg ) }}
Solo mire: podemos leer eso como si fuera un inglés sencillo. Cualquiera debería poder entender lo que está sucediendo aquí. Ahora, cuando nuestro componente principal parezca listo, es hora de cambiar el enfoque a los secundarios.
Los ejes deberían simplemente devolver líneas, nada complejo allí. Según la especificación SVG , para crear una línea, necesitamos pasar cuatro coordenadas x1, y1, x2, y2
:. Y tenga en cuenta que los ejes pueden ser verticales u horizontales y deben respetar la posición inicial por la que se pasa props
:
Aquí está scripts/components/axis.js
:
import React from 'react';export default class Axis extends React.Component { prepareCords() { let coords = { x1: this.props.x, y1: this.props.y } if(this.props.horizontal) { coords.x2 = coords.x1 + this.props.length; coords.y2 = coords.y1; } else { coords.x2 = coords.x1; coords.y2 = coords.y1 + this.props.length; } return coords; } render() { let coords = this.prepareCords(); return ( line {...coords} stroke="green" strokeWidth={2} / ) }}
Aquí {…coords}
hay una nueva y elegante forma de escribir en ES6. x1={coords.x1} x2={coords.x2} y1={coords.y1} y2={coords.y2}.
Gracias a Babel, podemos usarlo sin esperar a que los navegadores lo implementen.
Sólo para probar que el eje funciona, eliminemos una implementación del cuerpo del gráfico:
import React from 'react';export default class GraphBody extends React.Component { render() { return null; }}
Regresar null
en este caso obligará a React a representar una noscript
etiqueta. Podemos lograr el mismo resultado "vacío" usando return g /
, que devolverá un grupo SVG vacío. Juegos porno
Los grupos en SVG son algo así como div
elementos en HTML, muy útiles cuando su componente debe devolver más de un nodo. De forma predeterminada, esto no funcionará en JSX (solo se devolverá el último nodo), por lo que envolveremos todo en un g
elemento para evitar esto.
En este momento en nuestro navegador deberíamos ver dos ejes:
El siguiente paso es quitar el talón y crear un cuerpo de gráfico completamente funcional. Para dibujar una línea gráfica, usaremos una ruta . Esto requiere que pasemos una cadena especialmente diseñada como d
parámetro. Elaborar esta cuerda es fácil; Consta de dos partes: un Moveto
comando inicial y un montón de Lineto
comandos para dibujar el gráfico en sí:
Moveto
será nuestro punto de partida: M ${this.props.x} ${this.props.y}
. Esto moverá nuestro pincel a las coordenadas iniciales. Luego, conectaremos cada punto de datos junto con el L x y
comando.
Sin embargo, no podemos pasar x
y y
tal como los obtenemos del conjunto de datos. Necesitamos sumarlos con un punto inicial x
y restarlos del punto inicial y
, porque el eje y en SVG va de arriba a abajo.
El código resultante se ve así:
import React from 'react';export default class GraphBody extends React.Component { static defaultProps = { multiplier: 20 }; prepareData() { let d = [`M ${this.props.x} ${this.props.y}`]; let collector = this.props.data.map(chunk = { let xNext = this.props.x + chunk[0] * this.props.multiplier; let yNext = this.props.y - chunk[1] * this.props.multiplier; return `L ${xNext} ${yNext}`; }); return d.concat(collector).join(' '); } render() { let d = this.prepareData(); return( path d={d} stroke="orange" strokeWidth={1} fill="none" / ) }}
También multipliqué las coordenadas por una constante solo para hacer el gráfico más bonito.
Entonces, ¡estamos listos para enviar! Pero digamos que justo antes de eso, nuestros datos cambian. Supongamos que el departamento de ciencia de datos amplía nuestro conjunto de datos con otra matriz y nos pide que creemos una forma de cambiar datos sobre la marcha.
Nuestro nuevo data.js
se ve así:
export default [ [ [1, 3], [2, 5], [3, 2], [4, 16], [18, 5] ], [ [1, 16], [2, 23], [3, 5], [4, 3], [5, 1] ]];
Agregar soporte para múltiples conjuntos de datos es una tarea fácil para nosotros, gracias al enfoque de flujo de datos de arriba a abajo de React . Sólo necesitamos cambiar los datos que le estamos pasando al Graph
componente de forma dinámica; React hará el renderizado por nosotros.
Entonces, lo nuevo index.js
es esto:
import React from 'react';import ReactDOM from 'react-dom';import App from './app';import data from './data';ReactDOM.render(App datasets={data} /, document.getElementById('root'));
Y aquí está scripts/app.js
:
import React from 'react';import Graph from './components/graph';export default class App extends React.Component { render() { return ( Graph data={this.props.datasets[0]} / # or this.props.datasets[1] just to check that everything is working ) }}
Sin embargo, cambiar el conjunto de datos en el código no es nada fácil de usar (incluso si tenemos React Hot Load para actualizar mágicamente la página por nosotros). Entonces, agreguemos una opción para cambiar el conjunto de datos.
Aquí está scripts/app.js
:
import React from 'react';import Graph from './components/graph'export default class App extends React.Component { state = { dataSetIndex: 0 } selectDataset(event) { this.setState({dataSetIndex: event.target.value}); } render() { let options = this.props.datasets.map((_, index) = { return option key={index} value={index}Dataset {index + 1}/option }); return ( div select value={this.state.dataSetIndex} onChange={this.selectDataset.bind(this)} {options} /select Graph data={this.props.datasets[this.state.dataSetIndex]} / /div ) }}
Ahora nuestros mineros de datos están contentos; ¡pueden jugar con conjuntos de datos sobre la marcha!
Pero llega el mañana y ahora quieren poder descargar gráficos renderizados para trabajar sin conexión . Anteriormente, eso significaría mucho trabajo , pero React no tiene una dependencia DOM real, por lo que puedes renderizarlo en un servidor fácilmente.
Comenzamos creando una aplicación Express simple que maneja las solicitudes entrantes de gráficos SVG ( svg_server.js
):
require("babel-register");var express = require('express');var app = express();var data = require('./scripts/data').default;var svgRenderer = require('./scripts/svg_renderer').default;app.get('/svg', function (req, res) { var svg = svgRenderer(data[0]); res.send(svg);});var server = app.listen(3000, function () { var host = server.address().address; var port = server.address().port; console.log('Example app listening at https://%s:%s', host, port);});
Como puede ver, solo tres líneas son realmente de nuestra aplicación:
var data = require('./scripts/data');var svgRenderer = require('./scripts/svg_renderer');var svg = svgRenderer(data[0]);
Todas las demás líneas son solo el texto estándar y los ganchos de Express.
Y scripts/svg_renderer.js
se parecerá mucho a nuestra versión anterior del main App
:
import React from 'react';import ReactDOMServer from 'react-dom/server';import Graph from './components/graph'export default function(data) { return ReactDOMServer.renderToStaticMarkup(Graph data={data}/);}
Para probarlo, haríamos:
- correr
node svg_server.js
, - abierto
localhost:3000/svg
, - y, para estar completamente seguro, ejecutar
curl localhost:3000/svg
y recibir.
svgline x1="20" y1="500" x2="820" y2="500" stroke="green" stroke-width="2"/lineline x1="20" y1="0" x2="20" y2="500" stroke="green" stroke-width="2"/linepath d="M 20 500 L 40 440 L 60 400 L 80 460 L 100 180 L 380 400" stroke="orange" stroke-width="1" fill="none"/path/svg
¡Renderizado del lado del servidor!
Ahora, nuestro departamento de ciencia de datos nos ama plenamente y finalmente podemos irnos a casa. Si te perdiste algo, puedes encontrar el ejemplo completo en el repositorio .
Espero que este tutorial te muestre que, desde la perspectiva de React, no hay ninguna diferencia en qué renderizar . Puede aprovechar todas las ideas que dan forma a su HTML en SVG y tener componentes pequeños y comprensibles que cualquiera puede cambiar fácilmente sin romper ninguna dependencia externa.
¿Pero deberías crear tus propios sistemas gráficos desde cero? No, muchas soluciones excelentes se pueden ampliar fácilmente para que funcionen con React (e incluso integraciones completas, como reaccionar-d3 , por ejemplo). Mi esperanza es que, al hacer este gráfico, hayas llegado a comprender cómo funcionan estas integraciones internamente.
Una pequeña advertencia antes de terminar. Tenga en cuenta que React no admite todos los elementos SVG en este momento (existen algunas limitaciones y piezas faltantes ), pero probablemente encontrará que tiene lo que necesita para los escenarios más comunes. Para los menos comunes, React proporciona una manera de configurar innerHTML
un elemento a través de peligrosamenteSetInnerHTML , lo que puede ayudarlo a solucionar cualquier elemento SVG faltante que pueda necesitar. Además, parece que muchos de estos problemas se solucionarán en la próxima versión de React.
¡Feliz vectorización!
Otras lecturas
- Representación del lado del servidor con React, Node y Express
- Por qué debería considerar React Native para su aplicación móvil
- Repensar el SVG responsivo
- Cómo escalar las aplicaciones de React
- Un vistazo al futuro con React Native para web
(ds, jb, ml, al, mrn)Explora más en
- Codificación
- javascript
- SVG
- Reaccionar
Tal vez te puede interesar:
- ¿Deberían abrirse los enlaces en ventanas nuevas?
- 24 excelentes tutoriales de AJAX
- 70 técnicas nuevas y útiles de AJAX y JavaScript
- Más de 45 excelentes recursos y repositorios de fragmentos de código
Generando SVG con React
En este artículo, Ilya Zayats le mostrará que, desde la perspectiva de React, no hay ninguna diferencia en qué renderizar. React ayuda a organizar una aplic
programar
es
https://aprendeprogramando.es/static/images/programar-generando-svg-con-react-879-0.jpg
2025-01-14

Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente