Rendering D3 code that is not hardcoded

I notice that in most of the D3 documentation, charts, graphs, fields, etc. often hardcoded. XAxis - 500 pixels, etc. This is not very helpful to me. So I'm trying to think about how I can take a dynamic approach to rendering D3 content in React.

For example, in the following code, I simply present a row based on some time series pricing data. I have D3 code in componentDidMount , but given how D3 works, it needs specific width and height values. But in componentDidMount , I don't have these values ​​yet. Suppose this one-line plot is one of 100 other graphs, each in a div in a grid layout.

So how can I get div / svg width / height and only then calculate my D3 code and display svgs?

 componentDidMount() { console.log("componentDidMount") const data = this.props.data; const selectX = this.props.selectX; const selectY = this.props.selectY; console.log(data) const xScale = d3ScaleTime() .domain(d3ArrayExtent(data, selectX)) .range([0, 1]); const yScale = d3ScaleTime() .domain(d3ArrayExtent(data, selectY)) .range([1, 0]); const xAxis = d3AxisBottom() .scale(xScale) .ticks(data.length / 8); const yAxis = d3AxisLeft() .scale(yScale) .ticks(3); const selectScaledX = datum => xScale(selectX(datum)); const selectScaledY = datum => yScale(selectY(datum)); const sparkLine = d3Line() .x(selectScaledX) .y(selectScaledY); const linePath = sparkLine(data); console.log(linePath); this.setState({ linePath: linePath }); } 
+5
source share
1 answer

I tried to imitate your problem in the demo below.

 class Chart extends React.Component { componentDidMount() { var data = this.props.data; var containerDOMElementWidth = ReactDOM.findDOMNode(this).getBoundingClientRect().width var chartHeight = containerDOMElementWidth / 2; this.drawChart(data, containerDOMElementWidth, chartHeight); } drawChart(data, chartWidth, chartHeight) { var margin = { top: 30, right: 20, bottom: 30, left: 50 }; var width = chartWidth - margin.left - margin.right; var height = chartHeight - margin.top - margin.bottom; var parseDate = d3.timeParse("%d-%b-%y"); var x = d3.scaleTime().range([0, width]); var y = d3.scaleLinear().range([height, 0]); var xAxis = d3.axisBottom().scale(x) .ticks(2); var yAxis = d3.axisLeft().scale(y) .ticks(2); var valueline = d3.line() .x(function (d) { return x(d.date); }) .y(function (d) { return y(d.close); }); var svg = d3.select("body") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); data.forEach(function (d) { d.date = parseDate(d.date); d.close = +d.close; }); // Scale the range of the data x.domain(d3.extent(data, function (d) { return d.date; })); y.domain([0, d3.max(data, function (d) { return d.close; })]); svg.append("path").attr('class', 'line-chart') // Add the valueline path. .attr("d", valueline(data)); svg.append("g") // Add the X Axis .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") // Add the Y Axis .attr("class", "y axis") .call(yAxis); } render() { return <div></div>; } } function getRandomData() { return [{ date: "1-May-12", close: Math.random() * 90 }, { date: "30-Apr-12", close: Math.random() * 90 }, { date: "27-Apr-12", close: Math.random() * 90 }, { date: "26-Apr-12", close: Math.random() * 90 }, { date: "25-Apr-12", close: Math.random() * 90 }]; } ReactDOM.render( <div className="charts-container"> <div className="chart-wrapper"> <Chart data={getRandomData()} /> </div> <div className="chart-wrapper"> <Chart data={getRandomData()} /> </div> </div>, document.getElementById('container') ); 
 .line-chart { fill: none; stroke: blue } .charts-container { display: flex; } .chart-wrapper { width: 100%; } 
 <script src="https://unpkg.com/ react@16 /umd/react.development.js"></script> <script src="https://unpkg.com/ react-dom@16 /umd/react-dom.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script> <div id="container"> </div> 

Here we draw two diagrams one by one and calculate their width and height as follows:

 componentDidMount() { var data = this.props.data; // gets the width of container div element with ReactDOM.findDOMNode var containerDOMElementWidth = ReactDOM.findDOMNode(this).getBoundingClientRect().width // chart height have to be a half of width var chartHeight = containerDOMElementWidth / 2; // pass width and height as an arguments this.drawChart(data, containerDOMElementWidth, chartHeight); } 

Our drawChart method is as follows:

 drawChart(data, chartWidth, chartHeight) { var margin = { top: 30, right: 20, bottom: 30, left: 50 }; var width = chartWidth - margin.left - margin.right; var height = chartHeight - margin.top - margin.bottom; ... // code for the chart drawing 

render :

 ReactDOM.render( <div className="charts-container"> <div className="chart-wrapper"> <Chart data={getRandomData()} /> </div> <div className="chart-wrapper"> <Chart data={getRandomData()} /> </div> </div>, document.getElementById('container') ); 

If we display only one graph, it also works fine without changing the code, because we calculate the width of the diagram as the width of the parent div element:

 class Chart extends React.Component { componentDidMount() { var data = this.props.data; var containerDOMElementWidth = ReactDOM.findDOMNode(this).getBoundingClientRect().width var chartHeight = containerDOMElementWidth / 2; this.drawChart(data, containerDOMElementWidth, chartHeight); } drawChart(data, chartWidth, chartHeight) { var margin = { top: 30, right: 20, bottom: 30, left: 50 }; var width = chartWidth - margin.left - margin.right; var height = chartHeight - margin.top - margin.bottom; var parseDate = d3.timeParse("%d-%b-%y"); var x = d3.scaleTime().range([0, width]); var y = d3.scaleLinear().range([height, 0]); var xAxis = d3.axisBottom().scale(x) .ticks(2); var yAxis = d3.axisLeft().scale(y) .ticks(2); var valueline = d3.line() .x(function (d) { return x(d.date); }) .y(function (d) { return y(d.close); }); var svg = d3.select("body") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); data.forEach(function (d) { d.date = parseDate(d.date); d.close = +d.close; }); // Scale the range of the data x.domain(d3.extent(data, function (d) { return d.date; })); y.domain([0, d3.max(data, function (d) { return d.close; })]); svg.append("path").attr('class', 'line-chart') // Add the valueline path. .attr("d", valueline(data)); svg.append("g") // Add the X Axis .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") // Add the Y Axis .attr("class", "y axis") .call(yAxis); } render() { return <div></div>; } } function getRandomData() { return [{ date: "1-May-12", close: Math.random() * 90 }, { date: "30-Apr-12", close: Math.random() * 90 }, { date: "27-Apr-12", close: Math.random() * 90 }, { date: "26-Apr-12", close: Math.random() * 90 }, { date: "25-Apr-12", close: Math.random() * 90 }]; } ReactDOM.render( <div className="charts-container"> <div className="chart-wrapper"> <Chart data={getRandomData()} /> </div> </div>, document.getElementById('container') ); 
 .line-chart { fill: none; stroke: blue } .charts-container { display: flex; } .chart-wrapper { width: 100%; } 
 <script src="https://unpkg.com/ react@16 /umd/react.development.js"></script> <script src="https://unpkg.com/ react-dom@16 /umd/react-dom.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script> <div id="container"> </div> 

The same goes for the three diagrams:

 class Chart extends React.Component { componentDidMount() { var data = this.props.data; var containerDOMElementWidth = ReactDOM.findDOMNode(this).getBoundingClientRect().width var chartHeight = containerDOMElementWidth / 2; this.drawChart(data, containerDOMElementWidth, chartHeight); } drawChart(data, chartWidth, chartHeight) { var margin = { top: 30, right: 20, bottom: 30, left: 50 }; var width = chartWidth - margin.left - margin.right; var height = chartHeight - margin.top - margin.bottom; var parseDate = d3.timeParse("%d-%b-%y"); var x = d3.scaleTime().range([0, width]); var y = d3.scaleLinear().range([height, 0]); var xAxis = d3.axisBottom().scale(x) .ticks(2); var yAxis = d3.axisLeft().scale(y) .ticks(2); var valueline = d3.line() .x(function (d) { return x(d.date); }) .y(function (d) { return y(d.close); }); var svg = d3.select("body") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); data.forEach(function (d) { d.date = parseDate(d.date); d.close = +d.close; }); // Scale the range of the data x.domain(d3.extent(data, function (d) { return d.date; })); y.domain([0, d3.max(data, function (d) { return d.close; })]); svg.append("path").attr('class', 'line-chart') // Add the valueline path. .attr("d", valueline(data)); svg.append("g") // Add the X Axis .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") // Add the Y Axis .attr("class", "y axis") .call(yAxis); } render() { return <div></div>; } } function getRandomData() { return [{ date: "1-May-12", close: Math.random() * 90 }, { date: "30-Apr-12", close: Math.random() * 90 }, { date: "27-Apr-12", close: Math.random() * 90 }, { date: "26-Apr-12", close: Math.random() * 90 }, { date: "25-Apr-12", close: Math.random() * 90 }]; } ReactDOM.render( <div className="charts-container"> <div className="chart-wrapper"> <Chart data={getRandomData()} /> </div> <div className="chart-wrapper"> <Chart data={getRandomData()} /> </div> <div className="chart-wrapper"> <Chart data={getRandomData()} /> </div> </div>, document.getElementById('container') ); 
 .line-chart { fill: none; stroke: blue } .charts-container { display: flex; } .chart-wrapper { width: 100%; } 
 <script src="https://unpkg.com/ react@16 /umd/react.development.js"></script> <script src="https://unpkg.com/ react-dom@16 /umd/react-dom.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script> <div id="container"> </div> 
+2
source

Source: https://habr.com/ru/post/1273514/


All Articles