amcharts / amcharts3-react

Official amCharts V3 React component
Apache License 2.0
118 stars 50 forks source link

Chart's dataProvider state update makes the chart flashing on every update #21

Open vaidsu opened 7 years ago

vaidsu commented 7 years ago

Note sure if this is a bug, but here is the client code:

var MyChart = React.createClass({
    getInitialState: function () {
        return {
            dataProvider: {},
            timer: null
        };
    },

    generateData: function() {
        $.ajax({
            url: <url>,
            dataType: 'json',
            data: {},
            cache: false,
            success: function(data) {
                this.setState({dataProvider: data});
            }.bind(this),
            error: function(xhr, status, err) {
                console.error(this.props.url, status, err.toString());
            }.bind(this)
        });
    },

    componentDidMount: function () {
        var self = this;

        self.setState({
            // Update the chart dataProvider every 3 seconds
            timer: setInterval(function () {
                self.setState({
                    dataProvider: self.generateData()
                });
            }, 5000)
        });
        this.generateData();
    },

    componentWillUnmount: function () {
        clearInterval(this.state.timer);
    },

    render: function () {
        // Render the chart
        return React.createElement(AmCharts.React,
            {
                "type": "serial",
                "listeners": [ {
                    "event": "clickGraphItem",
                    "method": function(event) {
                        ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(document.getElementById('comp-data')));
                        ReactDOM.render(
                            <AdminTable component={event.item.dataContext.component} />,
                            document.getElementById("comp-data")
                        );
                    }
                }],
                "dataProvider": this.state.dataProvider,
                "guides": [],
                "valueAxes": [
                    {
                        "id": "ValueAxis-1",
                        "stackType": "regular",
                        "labelsEnabled": false
                    }
                ],
                "fontFamily": "Helvetica Neue, Helvetica, sans-serif",

                "colors": [  "#0D4D4D",  "#625E5E",  "#33A827",  "#FFBA0F",  "#D63A00",  ],
                "graphs": [

                    {
                        "fillAlphas": 1,
                        "lineAlpha": 0,
                        "type": "column",
                        "id": "Bar0",
                        "valueField": "noflag",
                        "labelsEnabled": false,
                        "balloonText": "[[category]] <b>[[value]]</b>",
                    },

                    {# SNIP #}

                ],
                "categoryField": "component",
                "categoryAxis": {
                    "labelsEnabled": false,
                    "lineAlpha": 0
                },
            }

        );
    }
});

When I use the above code, and every few seconds I update the state of the dataProvider. I see the graph is flashing. I switched off animation, even tried with skipEvents = true for the validateNow.

Please let me know if this is bug, I would like to contribute too.

Pauan commented 7 years ago

@vaidsu I'm sorry for taking so long to get back to you about this.

Inside of the setInterval you are using this code:

self.setState({
    dataProvider: self.generateData()
});

However, self.generateData() returns undefined, which means that dataProvider is now undefined, which means that the chart will now be blank.

Then after the $.ajax succeeds, it calls this.setState({dataProvider: data}); which causes the data to show up again. That is why it is flickering.

You can fix it by changing componentDidMount to this:

componentDidMount: function () {
    var self = this;

    self.setState({
        // Update the chart dataProvider every 3 seconds
        timer: setInterval(function () {
            self.generateData();
        }, 5000)
    });
    this.generateData();
},

It doesn't call setState inside of setInterval, so the flicker is gone.

vaidsu commented 7 years ago

Sorry from my side to respond late. @Pauan Will try this out and let you know. I am going to use this in a big app. Amcharts is been my chart tool for a long time. Thanks for this wrapper.

Hoping to see if I can contribute something, mainly w.r.t the conversion of dataProvider objects pattern back and forth. For instance, what if we can make the input dataProvider from user side is uniform, but the plugins would convert the data into the entity of choice like pie, area, bar, but the user need not think about how to reform his/her data.

Anyway, I will be back with some more info, probably this capability is already there.

lipp commented 7 years ago

Hi! I am having the same issue. I guess this piece of code is the problem:

 // TODO is this correct ? should this use componentWillUpdate instead ?
    componentDidUpdate: function (oldProps) {
      var didUpdate = updateObject(this.state.chart, oldProps, this.props);

      // TODO make this faster
      if (didUpdate) {
        this.state.chart.validateNow(true);
      }
    }

I think this is:

SaraNaifar commented 6 years ago

any thing new about this issue ?

LeandroVCastro commented 5 years ago

Me too