Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Options object not being changed after setState #64

Open
marcelobaez opened this issue Nov 21, 2017 · 6 comments
Open

Options object not being changed after setState #64

marcelobaez opened this issue Nov 21, 2017 · 6 comments

Comments

@marcelobaez
Copy link

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

@Pauan
Copy link
Collaborator

Pauan commented Nov 22, 2017

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

@marcelobaez
Copy link
Author

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.

@Pauan
Copy link
Collaborator

Pauan commented Nov 25, 2017

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.

@raymondsiu
Copy link

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
Copy link

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

@dcortesBCN
Copy link

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"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants