7

Using React functional components, I have not been able to find a way to animate my chart with dynamic data received asynchronously. The sandbox below illustrates the problem with a timer simulating the asynchronous read.

https://codesandbox.io/s/basic-column-chart-in-react-canvasjs-0gfv6?fontsize=14&hidenavigation=1&theme=dark

When running the example code, you should see 5 vertical bars of increasing heights animate. Then, after 5 seconds, it switches immediately to 4 bars of descending heights. I am looking to have that update animate.

Here is some reference information I've reviewed: CanvasJS React Demos: many of which animate on initial draw, but I couldn't find one that animates with dynamic data loaded after the initial render.

Chart using JSON Data is an demo that has dynamic data, but doesn't animate.

Reviewing the CanvasJS forum, I found a couple links, but none that address React functional components

Vishwas from Team Canvas said:

To update dataPoints dynamically and to animate chart, you can instantiate the chart, update dataPoints via chart-options and then call chart.render as shown in this updated JSFiddle.

var chart = new CanvasJS.Chart("chartContainer", {
  title: {
    text: "Animation test"
  },
  animationEnabled: true,  
  data: [{
    type: "column",
    dataPoints: []
  }]
});

chart.options.data[0].dataPoints =  [{ label: "Apple", y: 658 },
                                     { label: "Orange", y: 200 },
                                     { label: "Banana", y: 900 }];
chart.render();

This sample is pure JS, but I tried to adapt the principle to my React functional component. To better comport with React best practices, I incorporated the useState hook for storing the data and the useEffect hook to handle the fetch. But, alas, I couldn't get my sandbox to animate with the dynamic data.

I think the problem is that CanvasJS expects to animate only on the first render, as stated by Sanjoy in the CanvasJS forum on 7/19/2016.

I found this SO question from Jan 2015 that suggests:

My current ugly workaround is to reinstantiate the chart every time I update just to achieve that animation effect.

I'm hopeful that the situation has improved in the last four years, but if this hack is still the best/only way to go, I need some guidance on how to reinstantiate the chart every time using a React functional component.

Jay Cincotta
  • 4,298
  • 3
  • 21
  • 17
  • You need to dispose (destroy) react chart component and they re create it. To do that you will need one root App class then created child component (chrts). This is `only way` (with react) not for this case but for manu others.In your case example i see procedural way of using react. Look at https://github.com/zlatnaspirala/react-vs-typescript-starter – Nikola Lukic Jan 03 '20 at 22:18

3 Answers3

1

To force a remount of a component pass a different key when you want to remount the component

<CanvasJSChart
  key={dataPoints.toString()} // force a remount when `dataPoints` change
  containerProps={containerProps}
  options={options}
  onRef={ref => (chart.current = ref)}
/>

Working example

Asaf Aviv
  • 11,279
  • 1
  • 28
  • 45
-1

Both examples works fine.

You can always animate chars with some kind of calling. I use in this case setInterval.

<script src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
 
<script>
 
 var chart;
 
window.onload = function () {

  chart = new CanvasJS.Chart("chartContainer", {
   title: {
     text: "Animation test"
   },
   animationEnabled: true,  
   data: [{
     type: "column",
     dataPoints: []
   }]
 });

 chart.options.data[0].dataPoints =  [{ label: "Apple", y: 0 },
                                     { label: "Orange", y: 0 },
                                     { label: "Banana", y: 0 }];
 chart.render();

}

var max = 0;
var s = {c: 0, i: 0};
function ANIMATE() {
 
    if (typeof chart === 'undefined') return;
    
    chart.options.data[0].dataPoints.forEach(function(item, index, array) {
    
      if (index == s.i) {
         array[index].y += 3;
         s.c++;
      }
      
      if (s.c > 12) {
        s.i++;
        s.c = 0;
        if (s.i == 15) { s.i = 0}
      }
      
    
    });
    
    if (max < 12) {
    chart.options.data[0].dataPoints.push({label: "apple" + Math.random(), y: 1 + Math.random() * 10});
     max++;
    }
    
   chart.render()
}


setInterval(function(){
 ANIMATE()
}, 1)

</script>

<div id="chartContainer" style="height: 370px; width: 100%;"></div>
Nikola Lukic
  • 4,001
  • 6
  • 44
  • 75
  • My question was about leveraging the animation capabilities built into CanvasJS via the `animationEnabled` property within a React functional component. This answer does not address the question. Please refer to this sandbox: https://codesandbox.io/s/basic-column-chart-in-react-canvasjs-0gfv6?fontsize=14&hidenavigation=1&theme=dark – Jay Cincotta Dec 18 '19 at 23:31
-1

I found a partial answer. Full executing code is in this code sandbox, but the critical bit is to delay the initial render of the chart until a state variable indicates that the data is available:

  return (
<div className="App">
  {!initialized ? (
    <h1> Loading...</h1>
  ) : (
    <CanvasJSChart containerProps={containerProps} options={options} />
  )}
</div>

);

This is only a partial solution because subsequent data updates still do not animate.

Jay Cincotta
  • 4,298
  • 3
  • 21
  • 17