amcharts3-react
amcharts3-react copied to clipboard
Options object not being changed after setState
I'm generating one <Amchart.React /> for each dataset that I have array of datasets. The state is being changed, however the graph never gets updated because of this condition never reach:
`var didUpdate = updateObject(this.state.chart, oldProps.options, this.props.options);
// TODO make this faster
if (didUpdate) {
this.state.chart.validateNow(true);
}`
So I asume the oldProps and the new Props are evaluating the same object. I've followed the examples to pass options objects
@marcelobaez Could you please show me the <AmCharts.React />
code that you're using to create the charts?
This is the code i've been using so far. Its a function wich returns one chart for each serie received:
`renderChart(serie) { const config = Object.assign({}, amChartCfg)
return (
<div className="col-xl-6" key={'chart-' + arguments[1]}>
<div className="row no-gutters">
<div className="card col-xl-9">
<div className="card-img-top bg-cyan">
<AmCharts.React style={{ width: "100%", height: "200px" }} options={config} />
</div>
<div className="card-body">
<h6 className="text-left text-info">{'Sensor ' + this.state.sensorIds[arguments[1]]}</h6>
<div className="dropdown-divider"></div>
<small className="text-muted">
<i className="fa fa-power-off text-success" aria-hidden="true"></i> Online
</small>
</div>
</div>
<div className="card col-xl-3">
<div className="card-body bg-grey"></div>
</div>
</div>
</div>
)
}`
This produces charts dynamically for each serie passed to the function. But when the options object is changed (via this.setState), the charts wont update.
The problem is that Object.assign({}, amChartCfg)
creates a shallow copy of the configuration, but AmCharts requires a deep copy of the configuration.
There are three ways to create a deep copy:
-
Create the configuration object inside the
renderChart
method:const config = { type: "serial", dataProvider: this.state.dataProvider, ... };
This is the recommended way of creating the configuration.
-
Use a function or method to create the configuration from scratch every time:
function makeConfig(dataProvider) { return { type: "serial", dataProvider: dataProvider, ... }; } ... const config = makeConfig(this.state.dataProvider);
This is similar to option 1, except that it moves the chart configuration into a separate function / method.
-
Use a function which creates a deep copy. You can use
JSON.parse
andJSON.stringify
as a simple way to accomplish this:const config = JSON.parse(JSON.stringify(amChartCfg));
This is generally not recommended, because it causes things like event listeners to be removed.
I've been having the same issue as @marcelobaez . I'll try your suggestion @Pauan and let you know how it goes. Before this issue with past 3.x.x versions, I was having click events being triggered infinitely, but at least that has gone away now in the latest version.
@Pauan This has solved my issue.
In my case I am using Lodash, so I just use _.cloneDeep(config)
to ensure I get a deep copy of the initial chart config, before I start adding and modifying the data provider to get the results I need.
Thanks!
Hi I have a similar issue I think but i m not really sure what i m doing bad, here is my code:
`import React from 'react' import 'amcharts3' import 'amcharts3/amcharts/serial' import 'amcharts3/amcharts/pie' import AmCharts from '@amcharts/amcharts3-react' import {DXHyperLink} from '../components/customFields'
class Chart extends React.Component {
getMinPeriodFromData = (frequency) => { if (frequency === "Y") return "YYYY"; else if (frequency === "M") return "MM"; else if (frequency === "W") return "DD"; else if (frequency === "D") return "DD"; else if (frequency === "H") return "hh"; else return "mm"; };
baseChartSeriesStructure = (dataProvided, frequency) => { return { "lineThickness": 2, "type": "serial", "fontFamily": "Arial", "theme": "light", "dataDateFormat": "YYYY-MM-DDTHH:NN:SS", "legend": { "useGraphSettings": true }, "graphs": [], "categoryField": "ts", "categoryAxis": { "parseDates": true, "gridPosition": "start", "axisAlpha": 0, "fillAlpha": 0.05, "fillColor": "#000000", "gridAlpha": 0, "minPeriod": this.getMinPeriodFromData(frequency),
},
"dataProvider": dataProvided,
"zoomControl": {
"zoomControlEnabled": true
},
"mouseWheelZoomEnabled": true,
"chartCursor": {
"cursorPosition": "mouse",
"zoomable": true
},
};
};
chartLine = (dataProvided, graphs, frequency) => { let defaultSeriesOptions = this.baseChartSeriesStructure(dataProvided, frequency); defaultSeriesOptions["graphs"] = graphs.map(graph => {
return {
"lineThickness": 2,
"fontFamily": "Arial",
"balloonText": "[[ts]]<br><b><span style='font-size:14px;'>[[v]]</span></b>",
"bullet": "none",
"lineColor": graph.color,
"negativeLineColor": graph.color,
"type": "line",
"valueField": graph.name,
"title": graph.name,
}
});
return defaultSeriesOptions;
};
chartBar = (dataProvided, graphs, frequency) => { let defaultSeriesOptions = this.baseChartSeriesStructure(dataProvided, frequency); defaultSeriesOptions["graphs"] = graphs.map(graph => {
return {
"minorGridEnabled": false,
"balloonText": "[[ts]]<br><b><span style='font-size:14px;'>[[v]]</span></b>",
"lineColor": graph.color,
"type": "column",
"valueField": graph.name,
"title": graph.name,
"lineAlpha": 0.2,
"fillAlphas": 0.8
}
});
return defaultSeriesOptions;
};
chartStackedBar = (dataProvided, graphs, frequency) => { let defaultSeriesOptions = this.baseChartSeriesStructure(dataProvided, frequency); defaultSeriesOptions["graphs"] = graphs.map(graph => {
return {
"balloonText": "[[ts]]<br><b><span style='font-size:14px;'>[[v]]</span></b>",
"type": "column",
"valueField": graph.name,
"title": graph.name,
"lineAlpha": 0.2,
"fillAlphas": 0.8,
"labelText": "[[value]]",
"lineColor": graph.color,
}
});
defaultSeriesOptions["valueAxes"] = [{
"stackType": "regular",
"axisAlpha": 0.3,
"gridAlpha": 0
}];
return defaultSeriesOptions;
};
getOptionsByType = (typeChart, dataProvided, graphs, frequency) => { if(typeChart === 'bar'){ return this.chartBar(dataProvided, graphs, frequency); } else if (typeChart === 'stacked-bar'){ return this.chartStackedBar(dataProvided, graphs, frequency); } else{ return this.chartLine(dataProvided, graphs, frequency); } };
mapDataToObject = data => data.map(elem => { let ele = {}; elem.entrySeq().forEach((k)=> { ele[k[0]] = k[1] }); return ele; });
render() { const { data, graphs, typeChart, changeTypeChart, availableTypes, frequency} = this.props;too const dataProvided = this.mapDataToObject(data); const options = this.getOptionsByType(typeChart, dataProvided, graphs, frequency);
return (
<div className="chart">
<AmCharts.React
style={{
width: "100%",
height: "500px"
}}
options={options}
/>
<div className="icon-group float-right">
{availableTypes.map((type,i) => (
<DXHyperLink
key={i}
className={type === typeChart ? "btn icon optionChart info-tip active": "btn icon optionChart info-tip"}
iconClassName={"icon-"+type}
onClick={() => changeTypeChart(type)}
/>
))}
</div>
</div>
)
} }
Chart.propTypes = {}; export default Chart `
My problem is when I change the type of chart from bar or line to stacked bar and everythink crush then But if I use first the stacked bar is deployed correctly
Im not sure why but it looks like each time I change (add or remove) a key in the options json it crush
I have proved all the suggested options and any of them works for me
By the way my amcharts react version is "@amcharts/amcharts3-react": "^3.0.4", "react": "^15.5.4"