1

I'm building a React-based admin dashboard that displays user engagement over time using Chart.js. The chart should update dynamically when new data is fetched from an API.

import React, { useRef, useEffect } from 'react';
import { Chart } from 'chart.js/auto';

const LineChart = ({ data }) => {
  const chartRef = useRef(null);

  useEffect(() => {
    const ctx = chartRef.current.getContext('2d');
    const chartInstance = new Chart(ctx, {
      type: 'line',
      data: {
        labels: data.labels,
        datasets: [
          {
            label: 'Users',
            data: data.values,
            borderColor: 'blue',
          },
        ],
      },
    });
  }, [data]);

  return <canvas ref={chartRef} />;
};

export default LineChart;

When the data prop changes, the chart overlaps with the previous instance rather than updating cleanly.

2 Answers 2

1

It's impractical to create a new instance on each data change, it's persistent through the component lifecycle. Instead, data needs to be updated on existing instance. The instance needs to be cleaned up on component unmount in order to prevent memory leaks:

  ...
  const chartRef = useRef(null);
  const chartInstance = useRef(null);

  useEffect(() => {
    const ctx = chartRef.current.getContext('2d');
    chartInstance.current = new Chart(...);
 
    return () => chartInstance.current.destroy();
  }, []);

  useEffect(() => {
    chartInstance.current.dataset.data = data.values;
    chartInstance.current.update();
  }, [data]);
  ...
Sign up to request clarification or add additional context in comments.

Comments

1

You can destroy the previous chart before creating a new one

import React, { useRef, useEffect } from 'react';
import { Chart } from 'chart.js/auto';

const LineChart = ({ data }) => {
  const chartRef = useRef(null);
  const chartInstanceRef = useRef(null); // Store the Chart instance

  useEffect(() => {
    const ctx = chartRef.current.getContext('2d');

    // Destroy the previous chart if it exists
    if (chartInstanceRef.current) {
      chartInstanceRef.current.destroy();
    }

    // Create new chart
    chartInstanceRef.current = new Chart(ctx, {
      type: 'line',
      data: {
        labels: data.labels,
        datasets: [
          {
            label: 'Users',
            data: data.values,
            
          },
        ],
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
      },
    });
  }, [data]);

  return <canvas ref={chartRef} />;
};

export default LineChart;

3 Comments

Why are irrelevant parts "borderColor: 'blue'" there? I didn't downvote, but this looks like AI-generated code, which is prohibited on SO. Destroying an instance on each data change is an overkill. And clean up is not "optional", and there's no need for two destroy()
Honestly, it's a bit frustrating to see my answer being labelled as AI-generated. I spent time writing this based on my own experience, and I didn't expect to have to defend that. Feedback on the code is fair, but labeling it like that without any real basis feels unfair and discouraging
It appeared very shortly after the question was asked with exhaustive details, this is often the sign of unreviewed AI code that can be seen on SO. I'm glad to hear this isn't so and this wasn't an accusation. Any way, the current edition has a problem, it previously contained cleanup function that is necessary in this case but was removed, it wasn't optional

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.