기록하는 개발자

[React] React+typescript에서 chartJs의 update() 사용법 본문

Web/React

[React] React+typescript에서 chartJs의 update() 사용법

밍맹030 2023. 4. 7. 16:57
728x90

프로젝트를 할 때마다 그래프가 필요할 때 라이브러리 ‘chartJs’의 Bar chart를 사용하였는데, 그냥 javascript가 아닌 react+typescript 환경에서 사용할 때 상태 변경에서 문제가 발생했다.

 

<Bar options={options} data={data} width="894px" height="320px" />

react+typescript 환경에서는 list를 위와 같이 라이브러리의 인자 ‘data’로 넘겨준다. 편의상 기존 data를 prevData, 변경된 data를 newData라고 하겠다.

→ 이 때 prevData가 변경되면 그래프에 newData 가 아닌 기존의 prevDatanewData 가 함께 보이는 문제가 있었다.

 

알아보니 chartJs의 javascript 버전에는 위 문제해결에 update() 함수가 사용 되는데 react 라이브러리에서는 사용이 불가한 함수이었다. 때문에 결국 canvas를 통해 그래프를 그리는 javascript 버전의 chartJs를 사용하여 그래프를 구현하였다.

 

 

결과 화면

설치

npm install react-chartjs-2 chart.js

 

statisticData

chart의 data 로 들어가게 될 객체의 구조로, 나는 가로 방향의 barChart를 만들기 위해 axis를 y로 주었다.

 

- labels : 그래프 왼쪽에 들어갈 label 배열로 Array<string> 형태

- datasets > data : 그래프에 들어갈 data의 지표로 Array<number> 형태

- datasets > backgroundColor : 각 data의 배경색으로 Array 형태

{ 
    labels: statisticsDataLabels,
    datasets: [
        {
            axis: "y",
            data: statisticsDataContent,
            backgroundColor: statisticsDataColor,
            borderRadius: Number.MAX_VALUE,
            maxBarThickness: 20,
            borderSkipped: false,
        },
    ],
}

 

BarChartView.tsx

import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  BarElement,
  Title,
  Tooltip,
  Legend,
} from "chart.js";
// 라이브러리 등록
ChartJS.register(
  CategoryScale,
  LinearScale,
  BarElement,
  Title,
  Tooltip,
  Legend
);

const BarChartView = ({ canvasCallback, height }: type.barchartViewProps) => {
  return (
    <div>
      <div id="canvasContainer">
        <canvas
          id="canvas"
          ref={canvasCallback}
          height={height}
          width={400}
        ></canvas>
      </div>
    </div>
  );
};
export default BarChartView;

- chart가 들어갈 canvas 태그를 선언하고 ref를 통해 canvas 를 가져오도록 한다.

→ useRef() 를 사용하여 Ref 객체를 만들고, 이 객체를 우리가 선택하고 싶은 DOM 에 ref 값으로 넣어주면, Ref 객체의 current 값은 우리가 원하는 DOM 을 가리키게 된다.

 

BarChart.tsx

import { useEffect, useRef } from "react";
import BarChartView from "./BarChartView";

import Chart from "chart.js/auto";
import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  BarElement,
  Title,
  Tooltip,
  Legend,
} from "chart.js";
ChartJS.register(
  CategoryScale,
  LinearScale,
  BarElement,
  Title,
  Tooltip,
  Legend
);

const BarChart = ({ statisticsList, statisticsData }: type.barChartProps) => {
  const chartRef = useRef<Chart | null>(null);

  useEffect(() => {
    const chart = chartRef.current;
    if (chart) {
      chart.data = statisticsData;
      chart.update();
    }
  }, [statisticsData]);

  const canvasCallback = (canvas: HTMLCanvasElement | null) => {
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (ctx && !chartRef.current) {
      chartRef.current = new Chart(ctx, {
        type: "bar",
        data: statisticsData,
        options : {...}
        },
      });
    }
  };

  const chartHeight = statisticsList ? statisticsList.length * 100 : 600;

  return (
    <BarChartView
      canvasCallback={canvasCallback}
      height={chartHeight}
    ></BarChartView>
  );
};
export default BarChart;

 

1. chartRef 선언

const chartRef = useRef<Chart | null>(null);

- chartRef는 Chart가 생성되기 전까지는 null로 존재한다.

 

2. canvasCallback

  const canvasCallback = (canvas: HTMLCanvasElement | null) => {
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (ctx && !chartRef.current) {
      chartRef.current = new Chart(ctx, {
        type: "bar",
        data: statisticsData,
        options : {...}
        },
      });
    }
  };

canvas 내 그래프를 그리기 위해 변수 ctx에 canvas.getContext("2d") 를 가져온다.

ctx가 존재하고, chartRef.current가 존재하지 경우 new Chart 함수를 통해 chartRef.current 에 그래프를 생성한다.

     chartRef.current가 없어야 하는 이유

    - 이미 chart가 존재하는데 또 new Chart 로 chart를 생성하면 에러가 발생한다. 때문에 update 함수를 통해서만 차트 수정이 가능하다.

 

(options 코드는 길어서 생략했는데 필요한 사람들이 있을 수 있으니 아래 더보기 안에 코드를 넣어놨습니다.)

 

options code

더보기

options: {
          responsive: false,
          plugins: {
            legend: {
              display: false,
            },
          },
          indexAxis: "y",
          scales: {
            x: {
              ticks: {
                font: {
                  size: 18,
                },
              },
              grid: {
                display: false,
              },
              border: {
                display: false,
              },
            },
            y: {
              ticks: {
                font: {
                  size: 18,
                },
                color: "black",
              },
              grid: {
                display: false,
              },
              border: {
                display: false,
              },
            },
          },

 

 

3. useEffect

  useEffect(() => {
    const chart = chartRef.current;
    if (chart) {
      chart.data = statisticsData;
      chart.update();
    }
  }, [statisticsData]);

내가 사용하고자 했던 기능은 이 useEffect 내의 update 함수이다.

chartRef.current를 통해 이미 chart가 존재하는 경우 chart의 data를 변경하고 update 함수를 호출한다.

의존성 배열에 statisticsData를 넣어줌으로써 그래프에 들어갈 statisticsData가 변경될 때마다 useEffect 내부 동작을 수행하도록 하였다.

chart가 null인 경우 즉, chart가 아직 생성되지 않은 경우에는 조건문 내부의 동작이 실행되지 않는다.

 

 

참고

https://www.chartjs.org/docs/latest/

 

Chart.js | Chart.js

Chart.js Welcome to Chart.js! Why Chart.js Among many charting libraries (opens new window) for JavaScript application developers, Chart.js is currently the most popular one according to GitHub stars (opens new window) (~60,000) and npm downloads (opens ne

www.chartjs.org

https://stackoverflow.com/questions/66733424/chartjs-chart-in-typescript-object-is-possibly-undefined-in-useeffect

 

ChartJs chart in typescript: Object is possibly undefined in useEffect

I'm trying to update a chartjs using the useEffect hook. However my react page is crashing saying: TypeError: Cannot read property 'data' of undefined Here is the code: const MyChart = ({ chartDa...

stackoverflow.com

 

728x90