kirjs / react-highcharts

React wrapper for Highcharts library
http://kirjs.github.io/react-highcharts/
MIT License
1.26k stars 233 forks source link

Animation not working when config data changes with filter #353

Closed andreoliveira56 closed 6 years ago

andreoliveira56 commented 6 years ago

Hi!

Imagine that I have a dropdown, a Pie Chart and the config data lives outside the component that renders the Pie Chart.

Then, when I change the filter, the config data changes and the Chart should re-render. The problem is: The Chart re-renders without animation! The chart just appears.

I tried this: https://github.com/kirjs/react-highcharts/issues/171#issuecomment-268418008, but it just re-renders the Chart and it animates as is a new Chart.

I would like to have a smooth animation like when we click on the Legend. Is that possible?

Thank you.

ilyjs commented 6 years ago

You need

https://github.com/kirjs/react-highcharts#accessing-highcharts-api-after-render

It works for me.

import React, {Component} from 'react'
import HighchartsExporting from 'highcharts-exporting'
import HighchartsMore from 'highcharts-more'
import Highcharts from 'highcharts'
import ReactHighcharts from 'react-highcharts'

export default class Example extends Component {

    setData = () => {
        let chart = this.refs.chart.getChart();
        chart.series[0].update({
            data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]
        });
    }

    render() {
        let config = {
            chart: {
                events: {
                    addSeries: function () {
                        let label = this.renderer.label('A series was added, about to redraw chart', 100, 120)
                            .attr({
                                fill: Highcharts.getOptions().colors[0],
                                padding: 10,
                                r: 5,
                                zIndex: 8
                            })
                            .css({
                                color: '#FFFFFF'
                            })
                            .add();

                        setTimeout(function () {
                            label.fadeOut();
                        }, 1000);
                    }
                }
            },

            xAxis: {
                categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
            },

            series: [{
                data: [216.4, 194.1, 95.6, 54.4, 29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5]
            }]
        }

        HighchartsMore(ReactHighcharts.Highcharts);
        HighchartsExporting(ReactHighcharts.Highcharts);
        let charts = <ReactHighcharts config={config} ref="chart"> </ReactHighcharts>
        return (
            <div>
                <button onClick={this.setData}>Change data</button>
                {charts}
            </div>
        )
    }

}
andreoliveira56 commented 6 years ago

@ilyjs It would work if my data lives in the component. But my data comes from outside the component.

Something like:

let { value } = this.props;

config = {
    chart: {
      type: 'pie'
    },
    series : [{
       name: 'Series Name',
       ....
       data: [{
         name: 'Name 1',
         y: value
       } , {
         ...
       }]
    }]
  };

return <HighChart config={config} />

With a code like this, the chart just re-renders but without animation: it just appears with the new data.

adamlawrencium commented 6 years ago

The animations are a major feature of highcharts, and should be included in any wrapper. Check out https://github.com/whawker/react-jsx-highcharts. I'm still experimenting with both to see which offers better animations.

ilyjs commented 6 years ago

@andreoliveira56 I understood you .

You can't do this let { value } = this.props; in config : y: value. In this case, the graph will always be reset.

You need to use componentWillReceiveProps (Learn more) and neverReflow={true}. And most importantly https://github.com/kirjs/react-highcharts#accessing-highcharts-api-after-render

1) Without Redux

import React, {Component} from 'react'
import Highcharts from 'highcharts'
import ReactHighcharts from 'react-highcharts'

export default class superChart extends Component {

     componentWillReceiveProps(nextProps){
         const { value } = nextProps
         this.setData(value)
     }

     setData(value){
         let chart = this.refs.chart.getChart();
         chart.series[0].update({
             data: value
         });
     }

     render() {
        let config = {
            chart: {
                events: {
                    addSeries: function () {
                        let label = this.renderer.label('A series was added, about to redraw chart', 100, 120)
                            .attr({
                                fill: Highcharts.getOptions().colors[0],
                                padding: 10,
                                r: 5,
                                zIndex: 8
                            })
                            .css({
                                color: '#FFFFFF'
                            })
                            .add();

                        setTimeout(function () {
                            label.fadeOut();
                        }, 1000);
                    }
                }
            },

            xAxis: {
                categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
            },

            series: [{
                data: this.props.value
            }]
        }

        let charts = <ReactHighcharts neverReflow={true} config={config} ref="chart"> </ReactHighcharts>
        return (
            <div>
                {charts}
            </div>
        )
    }
}

2) With Redux

import React, {Component} from 'react'
import Highcharts from 'highcharts'
import ReactHighcharts from 'react-highcharts'
import {connect} from "react-redux";
import {sideChart} from "../../src/AC";

 class superChart extends Component {

    getData = () => {
        const {sideChart} = this.props
        sideChart(this.refs.chart.getChart())
    }

    let {superChart} = this.props
     render() {
        let config = {
            chart: {
                events: {
                    addSeries: function () {
                        let label = this.renderer.label('A series was added, about to redraw chart', 100, 120)
                            .attr({
                                fill: Highcharts.getOptions().colors[0],
                                padding: 10,
                                r: 5,
                                zIndex: 8
                            })
                            .css({
                                color: '#FFFFFF'
                            })
                            .add();

                        setTimeout(function () {
                            label.fadeOut();
                        }, 1000);
                    }
                }
            },

            xAxis: {
                categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
            },

            series: [{
                data: superChart
            }]
        }

        let charts = <ReactHighcharts neverReflow={true} config={config} ref="chart"> </ReactHighcharts>
        return (
            <div>
                <button onClick={this.getData}>Change data</button>
                {charts}
            </div>
        )
    }
}

export default connect((state) => {
    return {
        superChart:state.chat.updateData
    }
}, {sideChart})(superChart)

In "../../src/AC"

...........

export function sideChart(chart) {
    return {
        type: SIDE_CHART,
        payload: { chart }
    }
}

in reducer

. . . . . 
const {type, payload, response} = action
    switch (type) {
case SIDE_CHART: {
payload.chart.series[0].update({
                    data: ''Any variable" 
                });
return 'Any State'
}
}
orbengigi commented 6 years ago

Hi, I'm having the same issue with the animations, I'm trying to get the data from out source and update it in the click event (of a point in the series), the data updates as required but the animation is not working (even after I tried what you suggested above)

my code is:

import React from 'react'; import ReactHighcharts from 'react-highcharts'; import Highcharts from 'highcharts';

export default class CustomChart extends React.Component { constructor(props) { super(props); this.state = { data: [], config: this.getChartConfiguration(props) }; }

getChartConfiguration(props) {
    return {
        chart: {
            type: props.type,
            events: {
                addSeries: function () {
                    let label = this.renderer.label('A series was added, about to redraw chart', 100, 120)
                        .attr({
                            fill: Highcharts.getOptions().colors[0],
                            padding: 10,
                            r: 5,
                            zIndex: 8
                        })
                        .css({
                            color: '#FFFFFF'
                        })
                        .add();

                    setTimeout(function () {
                        label.fadeOut();
                    }, 1000);
                }
            }
        },
        title: { text: props.title },
        xAxis: {
            type: 'category',
        },
        legend: {
            enabled: props.withLegend,
        },
        tooltip: {
            headerFormat:
                '<span style="font-size:11px">{series.name}</span><br>',
            pointFormat:
                '<span style="color:{point.color}">{point.name}</span>: <b>{point.y}</b>',
        },
        credits: {
            enabled: false
        },
        series: [
            {
                name: props.tooltipHeader,
                colorByPoint: true,
                showInLegend: props.withLegend,
                point: {
                    events: {
                        click: function (obj) {
                            props.handleClick(obj.point.name);
                        }
                    }
                },
                data: props.data
            },
        ]
    }
}

componentWillReceiveProps(props) {
    let chart = this.refs.chart.getChart();
    chart.series[0].update({ name: 'new title' });
    chart.series[0].setData(props.data);

}

render() {
    return (
        <ReactHighcharts neverReflow config={this.state.config} ref="chart" />
    );
}

}

I am using "highcharts": "^5.0.14", "react-highcharts": "^15.0.0",

any suggestions?

thanks much!

ilyjs commented 6 years ago

@orbengigi For highcharts 5.x.x use v. 13.0.0

orbengigi commented 6 years ago

I tried this: "highcharts": "5.0.14",
"react-highcharts": "13.0.0",

didn't work..

ilyjs commented 6 years ago

@orbengigi Sorry !!!

Use shouldComponentUpdate

shouldComponentUpdate(nextProps) {
    let chart = this.refs.chart.getChart();
    chart.series[0].update({ name: 'new title' });
    chart.series[0].setData(nextProps.data);
    return false;
}

You don't need neverReflow true.

Use it wisely! shouldComponentUpdate return false; Stops rendering this component. You can change it for your tasks

Learn more about shouldComponentUpdate

orbengigi commented 6 years ago

Sorry, still not working..

import React from 'react'; import ReactHighcharts from 'react-highcharts'; import Highcharts from 'highcharts';

//This component vhart type is defined in props //possible values: pie, column, bar.. export default class CustomChart extends React.Component { constructor(props) { super(props); this.state = { data: [], config: this.getChartConfiguration(props) }; }

shouldComponentUpdate(props) {
    let chart = this.refs.chart.getChart();
    chart.series[0].update({ name: 'new title' });
    chart.series[0].setData(props.data);
    return false;

}

getChartConfiguration(props) {
    return {
        chart: {
            type: props.type,
            events: {
                addSeries: function () {
                    let label = this.renderer.label('A series was added, about to redraw chart', 100, 120)
                        .attr({
                            fill: Highcharts.getOptions().colors[0],
                            padding: 10,
                            r: 5,
                            zIndex: 8
                        })
                        .css({
                            color: '#FFFFFF'
                        })
                        .add();

                    setTimeout(function () {
                        label.fadeOut();
                    }, 1000);
                }
            }
        },
        title: { text: props.title },
        xAxis: {
            type: 'category',
        },
        legend: {
            enabled: props.withLegend,
        },
        tooltip: {
            headerFormat:
                '<span style="font-size:11px">{series.name}</span><br>',
            pointFormat:
                '<span style="color:{point.color}">{point.name}</span>: <b>{point.y}</b>',
        },
        credits: {
            enabled: false
        },
        series: [
            {
                name: props.tooltipHeader,
                colorByPoint: true,
                showInLegend: props.withLegend,
                point: {
                    events: {
                        click: function (obj) {
                            props.handleClick(obj.point.name);
                        }
                    }
                },
                data: props.data
            },
        ]
    };
}

render() {
    return (
        <ReactHighcharts config={this.state.config} ref="chart" />
    );
}

}

am I doing anything wrong? still using these packages: "highcharts": "5.0.14", "react-highcharts": "13.0.0",

ilyjs commented 6 years ago

@orbengigi Format the code please, and better create an example in https://stackblitz.com

orbengigi commented 6 years ago

Thank for helping, I added this https://stackblitz.com/edit/react-5mwqst?file=Hello.js although it dosen't seem to load the chart.. got any idea?

ilyjs commented 6 years ago

@orbengigi It works . For example https://stackblitz.com/edit/react-pqs9kh . I will try to correct your example.

ilyjs commented 6 years ago

@orbengigi Done https://stackblitz.com/edit/react-sgotbg

orbengigi commented 6 years ago

Thanks, but this is not the behaviour I was looking for, trying to apply what you did to update the data in a pie click event that calls a callback

ilyjs commented 6 years ago

@orbengigi

Open the console and click on the pie

https://stackblitz.com/edit/react-sgotbg I think you need here. Soon we will update the demo and add various examples.

orbengigi commented 6 years ago

Great answer! This works great 😀

Thanks much for your help!

ilyjs commented 6 years ago

@orbengigi Great!