I have my main App.js component rendering a scatterplot with D3, which in turn renders a barplot, also with D3
When a point on the scatterpoint is clicked, based on the value of the point, one of 2 barplots is generated (on first click), or updated with new data (on subsequent clicks).
With the simplified code below, instead of updating the data on the original chart, a new bar plot is being appended to the div element with each click. How do I alter the componentDidUpdate() code in the barplot.js section so that the original barplot chart is updated?
//Data is input here
const scatterPlotData = [
[5, 20], [48, 90], [250, 50], [100, 33], [330, 95],
[410, 12], [475, 44], [25, 67], [85, 21], [220, 88]
];
const bardata1 = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];
const bardata2 = [ 1, 20, 33, 5, 7, 25, 22, 10, 1, 23,31, 2, 5, 40, 28, 27, 6, 8, 3, 5 ];
class ScatterPlot extends Component {
createScatterPlot() {
//d3 code here, relevant part which determines which barplot data to select
.on("click", function(d) {
//If x value of scatterplot point is >= 200, choose bar plot data above
if (d[0] >= 200) {
component.setState({ bardata: bardata2, event: d3.event });
} else {
component.setState({ bardata: bardata1, event: d3.event });
}
})
}
render() {
return (
<div>
{
//BarPlot class is called
this.state.bardata && this.state.event &&
<BarPlot bardata={this.state.bardata}/>
}
</div>
)
}
}
class BarPlot extends Component {
constructor(props){
super(props)
this.createBarPlot = this.createBarPlot.bind(this)
}
componentDidMount() {
this.createBarPlot()
}
componentDidUpdate(prevProps) {
if(JSON.stringify(this.props.bardata) !== JSON.stringify(prevProps.bardata)) {
this.setState({bardata: null});
this.createBarPlot()
}
}
createBarPlot() {
//d3 code here
}
render() {
return (
<div>
</div>
)
}
}
//Main app which calls the ScatterPlot class
class App extends Component {
constructor(props) {
super(props)
this.state = {
pointdata: pointdata1
}
}
render() {
return (
<div className="wrapper">
<header className="App-header">
Scatter Plot via React
</header>
<div id = 'plot-1'>
<ScatterPlot pointdata={this.state.pointdata} />
</div>
<div id="barplot"></div>
</div>
);
}
Related
I observed one issue with ngx-charts line chart, when i have single data point i.e
[
{
name: 'test',
series: [
{
name: 0,
value: 0,
},
],
},
]
Here is my html code:
#yAxisTicks = [0, 25, 50, 75, 100];
#xAxisTicks = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
#colorScheme: any = { domain: ['red'] };
<ngx-charts-line-chart
#chart
[yAxisTicks]="yAxisTicks"
[xAxisTicks]="xAxisTicks"
[scheme]="colorScheme"
[xAxis]="true"
[yAxis]="true"
[results]="single"
[yScaleMax]="100"
[tooltipDisabled]="true"
[view]="view"
>
</ngx-charts-line-chart>
The xAxisTicks are scattered and looks somewhat like bellow,
Does anyone came across this issue? Are there any fix available without removing the [xAxisTicks]="xAxisTicks"?
I want to use the Handsontable but I'm failing. I followed the instructions in the docs and came up with following code.
Report.js file
export const Report = () => {
const [data, setData] = useState([
["", "Ford", "Volvo", "Toyota", "Honda"],
["2016", 10, 11, 12, 13],
["2017", 20, 11, 14, 13],
["2018", 30, 15, 12, 13]
]);
const container = document.getElementById("reportTable");
console.log(container);
const Hot = new Handsontable(container, {
data: data,
rowHeaders: true,
colHeaders: true,
filters: true,
dropdownMenu: true
})
const onChange = ()=>{
setData([])
}
return (
document.body.style.backgroundColor = "#fff",
<div className="main-container">
<div className={styles.tableDiv}>
<p className={styles.reportText}>Report Your Daily Progress</p>
<div id="reportTable"></div>
</div>
</div>
)
}
However, I'm getting this error TypeError: Cannot read property 'ownerDocument' of null
and I don't know what I'm doing wrong. I'd appreciate your help. Thanks.
I want to create a char using c3 in React to, later on, be updated each second.
I am trying to follow the example provided by
Updating C3 charts on props change with React, but the first step, which is to create the chart, is not happening.
This is my fiddle:
https://jsfiddle.net/69z2wepo/227446/
import c3 from 'c3';
import React from "react";
import ReactDOM from "react-dom";
class Hello extends React.Component {
renderChart() {
this.chart = c3.generate({
bindto:"#chart1",
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
]
}
});
}
render() {
this.renderChart()
return <div id="chart1"></div>;
}
}
ReactDOM.render(
<Hello />,
document.getElementById('container')
);
I installed c3 with npm and am importing it in the component.
Thanks for the help.
In your example, it looks like the chart is generated before the selector div is even rendered, so the chart has no where to go. Instead of calling this.renderChart() in render(), you can call it componentDidMount. In that case, render will be called on the initial load, your <div id="chart1"></div> will be rendered and then renderChart will run, adding the SVG to the div.
As for updating the data, you can move the column data itself to state, then call setState with some new data and use componentDidUpdate to rerender the chart. That might look something like this:
class Chart extends React.Component {
constructor(props) {
super(props);
this.state = {
column1: ['data1', 30, 200, 100, 400, 150, 250],
column2: ['data2', 50, 20, 10, 40, 15, 25],
};
this.changeData = this.changeData.bind(this);
}
renderChart() {
c3.generate({
bindto: "#chart1",
data: {
columns: [this.state.column1, this.state.column2],
},
});
}
componentDidMount() {
this.renderChart();
}
componentDidUpdate() {
this.renderChart();
}
// Changes data to something arbitrary on button click
changeData() {
this.setState({
column1: ['data1', 70, 120, 30, 300, 230, 300],
column2: ['data2', 100, 120, 50, 140, 150, 80],
});
}
render() {
return (
<div>
<div id="chart1"></div>
<button onClick={this.changeData}>Change</button>
</div>
);
}
}
React Lifecycle methods are key here. Here's a handy chart linked from the docs: http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
I'm seeing an awkward bug using a third party library inside of a react component. I was able to reproduce a contrived demo for this post.
Let me start by explaining that I am using c3js - which is a charting library and rendering it in componentDidMount() and removing it in componentWillUnmount() with the correct calls to this.chart.destroy().
The bug occurs when filtering the components themselves, essentially what happens is the components are filtered correctly but the actual chart that sits inside the component stays the same as the first chart, which is very strange behaviour. Basically it's the wrong chart inside the wrong component!
You can see what I mean by clicking on the Remove all charts except chart 3 button, I have labeled the charts with a chartid and the filtering will correctly remove the other charts.
I am fairly certain it isn't my code because the filtering works correctly and updates the view. You can verify because I have labeled the charts and it is visible in the view. There is no console errors and I have verified my code works.
So my question is - can we work around this limitation using c3js, or is this really a problem with my code and the way I am rendering the charts.
Related demo: https://jsfiddle.net/69z2wepo/38614/
Related code:
var data = [
{
chartid: 1,
columns: [
['x', 0, 1, 2, 3, 4, 5, 6],
['data1', 130, 300, 330, 400, 300, 400, 500],
['data2', 390, 230, 200, 150, 100, 130, 210],
['data3', 290, 430, 300, 160, 210, 170, 190],
['data4', 190, 330, 200, 260, 190, 250, 320]
]
},
{
chartid: 2,
columns: [
['x', 0, 1, 2, 3, 4, 5, 6],
['data1', 130, 300, 330, 400, 300, 400, 500],
['data2', 390, 230, 200, 150, 100, 130, 210],
['data3', 290, 430, 300, 160, 210, 170, 190]
]
},
{
chartid: 3,
columns: [
['x', 0, 1, 2, 3, 4, 5, 6],
['data1', 130, 300, 330, 400, 300, 400, 500],
['data2', 390, 230, 200, 150, 100, 130, 210]
]
}
];
var Button = React.createClass({
handleDelete: function (id) {
this.props.handleDelete(id);
},
render: function() {
return (
<button onClick={this.handleDelete.bind(null, 3)}>
Remove all charts except chart 3
</button>
)
}
});
var Chart = React.createClass({
componentDidMount() {
this.chart = c3.generate({
bindto: '.chart-' + this.props.data.chartid,
data: {
columns: this.props.data.columns
}
});
},
componentWillUnmount() {
this.chart.destroy();
},
render: function() {
return (
<div>
<h4>{"chart-" + this.props.data.chartid}</h4>
<div className={"chart-" + this.props.data.chartid}>
</div>
</div>
)
}
});
var Child = React.createClass({
renderCharts: function(data) {
return data.map(function(metrics, i) {
return (
<Chart key={i} data={metrics} />
)
});
},
handleDelete: function(id) {
this.props.handleDelete(id);
},
render: function() {
return (
<div>
<Button handleDelete={this.handleDelete} />
{this.renderCharts(this.props.data)}
</div>
)
}
})
var App = React.createClass({
getInitialState: function() {
return {
initialData: this.props.data
}
},
handleDelete: function(id) {
var _filterFunc = function(data) {
if (data.chartid == id) return true;
return false;
};
var _filterCharts = Array.prototype.filter.call(this.state.initialData, _filterFunc);
this.setState({
initialData: _filterCharts
})
},
render: function() {
return (
<div>
<Child handleDelete={this.handleDelete} data={this.state.initialData} />
</div>
);
}
});
ReactDOM.render(
<App data={data} />,
document.getElementById('container')
);
The problem is the way you are setting the key on your chart. It's causing the renderer to get confused about which chart you're trying to keep.
Try this:
<Chart key={data[i].chartid} data={metrics} />
instead of <Chart key={i} data={metrics} />
Take a look at how React handles keys. Remember that you're uniquely identifying a child with a key for the lifecycle of the component. So since chart 1 is uniquely identified by key "1", you can't render chart 3 with key "1." My solution above ensures that the chart is uniquely identified by its chart id instead of by its rendering order.
I am trying to use gRapahel library to create bar chart in my web site project. I need to use this library because standard ajax control doesn't work in IE8. I have added all *.js files to my project and I also added below lines to web.config file:
<codeSubDirectories>
<add directoryName="CSCode"/>
<add directoryName="JSCode"/>
</codeSubDirectories>
When I tried to compile my project I got 3 errors in App_SubCode_JSCode.qgb8opkc.6.js file:
Only primitive types are allowed in a custom attribute
Unknown custom attribute class or constructor
Variable 'System' has not been declared
When I double click on the eroors it redirect me to the file with the erros which contains below lines:
//------------------------------------------------------------------------------
/// <autogenerated>
/// This code was generated by a tool.
/// Runtime Version: 4.0.30319.269
///
/// Changes to this file may cause incorrect behavior and will be lost if
/// the code is regenerated.
/// </autogenerated>
//------------------------------------------------------------------------------
[assembly: System.CodeDom.Compiler.GeneratedCodeAttribute("ASP.NET", "4.0.30319.272")]
[assembly: System.Security.SecurityRulesAttribute(System.Security.SecurityRuleSet.Level2)]
[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.0")]
Could You plase help me fix this issue?
Thanks in advance.
EDIT 1:
I have added below code to create chart but I got error:
Microsoft JScript runtime error: Object expected
on line:
var r = Raphael("InputHours"),
<script type="text/javascript">
window.onload = function () {
var r = Raphael("InputHours"),
fin = function () {
this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
},
fout = function () {
this.flag.animate({ opacity: 0 }, 300, function () { this.remove(); });
},
fin2 = function () {
var y = [], res = [];
for (var i = this.bars.length; i--; ) {
y.push(this.bars[i].y);
res.push(this.bars[i].value || "0");
}
this.flag = r.popup(this.bars[0].x, Math.min.apply(Math, y), res.join(", ")).insertBefore(this);
},
fout2 = function () {
this.flag.animate({ opacity: 0 }, 300, function () { this.remove(); });
},
txtattr = { font: "12px sans-serif" };
r.text(160, 10, "Single Series Chart").attr(txtattr);
r.text(480, 10, "Multiline Series Stacked Chart").attr(txtattr);
r.text(160, 250, "Multiple Series Chart").attr(txtattr);
r.text(480, 250, "Multiline Series Stacked Chart\nColumn Hover").attr(txtattr);
r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]]).hover(fin, fout);
r.hbarchart(330, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55]], { stacked: true }).hover(fin, fout);
r.hbarchart(10, 250, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55]]).hover(fin, fout);
var c = r.barchart(330, 250, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55]], { stacked: true, type: "soft" }).hoverColumn(fin2, fout2);
};
</script>
I found sollution for that (I think). I moved CSCode outsede App_Code folder and I am able to compile the project.