DEV Community

Vicente Canales
Vicente Canales

Posted on

Using Chart.js in a function component with React hooks

Why

When using React in conjunction with other JavaScript libraries, it's common to see that React-specific versions of those libraries exist. Regardless of whether they exist to make parts of those libraries available as components, or to aid the developer in making React play nice with a particularly intricate library, I think this is not the case for Chart.js.

Initialization

If we take advantage of React's useRef hook, we can make use of Chart.js without the need for a made-for-react version of it. The key snippet looks like this:

import React, { useEffect, useRef } from 'react';
import Chartjs from 'chart.js';

const chartConfig = {
  type: 'bar',
  data: {
    // ...
  },
  options: {
    // ...
  }
};

const Chart = () => {
  const chartContainer = useRef(null);
  const [chartInstance, setChartInstance] = useState(null);

  useEffect(() => {
    if (chartContainer && chartContainer.current) {
      const newChartInstance = new Chartjs(chartContainer.current, chartConfig);
      setChartInstance(newChartInstance);
    }
  }, [chartContainer]);

  return (
    <div>
      <canvas ref={chartContainer} />
    </div>
  );
};

export default Chart;

Chart.js uses the DOM Canvas to render, and the ctx parameter in new Chartjs(ctx, options) is a reference to the <canvas /> element where we'll mount our chart.

They key takeaway from this is that the useRef hook allows us to use an effect to initialize our chart as soon as the DOM object is available, by reacting to changes on the chartContainer ref variable.

After initialization

On initialization, we're assigning the initialized chart to the chartInstance state variable so that we can update our chart later, like this:

  const updateDataset = (datasetIndex, newData) => {
    chartInstance.data.datasets[datasetIndex].data = newData;
    chartInstance.update();
  };

  const onButtonClick = () => {
    const data = [1, 2, 3, 4, 5, 6];
    updateDataset(0, data);
  };

This updates the Chart after the callback for a button click is invoked, but this could also be done inside a useEffect callback, or really anywhere else where you have access to chartInstance.

There, short but sweet little tip!

PS. Check out the working version on codesandbox.io!

Top comments (5)

Collapse
 
ch543ch543 profile image
Tsai Yi Chun

Hi, Thank you for your article, it's really helpful!
While, I'm facing an issue now, that is I your method to build my multiple charts and updated all of them at the same time, but then I'm not able to modify my chart canvas when triggering the onHover function in charts, seems it only catch my chartInstance at the initial state, which is null, so then I cannot draw on my chartInstance when hovering on it, could you give an easy example if it is simple for you? Thank you very much!

Collapse
 
ch543ch543 profile image
Tsai Yi Chun

so far my solution is to create a state for hover event:
cont [hoverEvt, setHoverEvt] = useState(null)
and then in my chart config, I reset state whenever there is hover event:
chartConfig = {
...
options: {
onHover: function(evt) {setHoverEvt(evt)};
}}

And then useEffect() to draw my chart when the state is set:
useEffect(() => {
If(chartInstance != null) {
chartHover(evt, chartInstance);
}
}, [hoverEvt])

function chartInstance(evt, chartInstance) {
(draw my charts...)
}

But I'm thinking of if there is any better solution?
thank you!

Collapse
 
valtersantosmatos profile image
Valter Santos Matos

Nice post, ty :)

Collapse
 
afaqahmedkhan profile image
Afaq Ahmed Khan

Thanks mate, great help, short precise and helpful

Collapse
 
hs809 profile image
Hs809

In My project i am implementing this but the options are not applied in the doughnut charts. Can anyone help me out why this is happening.