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

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

Open vaidsu opened this issue 7 years ago • 5 comments

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.

vaidsu avatar Dec 12 '16 09:12 vaidsu

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

Pauan avatar Jan 24 '17 15:01 Pauan

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.

vaidsu avatar Feb 26 '17 08:02 vaidsu

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:

  • the wrong lifecycle hook (shouldbe componentWillReceiveProps)
  • causing the flicker, because if only dataProvider changes and nothing else, validateData() should be called and NOT validateNow (which seems to do a whole repaint).

lipp avatar Apr 27 '17 12:04 lipp

any thing new about this issue ?

SaraNaifar avatar Apr 13 '18 14:04 SaraNaifar

Me too

LeandroVCastro avatar Mar 22 '19 20:03 LeandroVCastro