amcharts3-react icon indicating copy to clipboard operation
amcharts3-react copied to clipboard

Options object not being changed after setState

Open marcelobaez opened this issue 7 years ago • 6 comments

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 avatar Nov 21 '17 18:11 marcelobaez

@marcelobaez Could you please show me the <AmCharts.React /> code that you're using to create the charts?

Pauan avatar Nov 22 '17 06:11 Pauan

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.

marcelobaez avatar Nov 23 '17 13:11 marcelobaez

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:

  1. 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.

  2. 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.

  3. Use a function which creates a deep copy. You can use JSON.parse and JSON.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.

Pauan avatar Nov 25 '17 04:11 Pauan

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.

raymondsiu avatar Nov 27 '17 14:11 raymondsiu

@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!

raymondsiu avatar Nov 27 '17 15:11 raymondsiu

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"

dcortesBCN avatar Mar 08 '18 18:03 dcortesBCN