[React + Typescript] Apache Echarts의 Bump Chart(Ranking) React, Typescript에서 사용하기

SangHyun Park·2024년 10월 4일
0

React

목록 보기
2/3

언급하지 않는 디테일한 echarts의 기능과 옵션은 공식 홈페이지를 참고 해주십시오.

부족한 부분이 많습니다. type사용이나 옵션생성에서 참고용으로 사용 해주시면 감사하겠습니다.

제 코드에서 가져왔기에 굵은 폰트 부분을 중점적으로 봐주십시오.

화면

요약

React와 Typescript를 사용.

Bump Chart란 마우스 호버시 해당하는 label의 그래프만 강조해서 보여주는 그래프이다.

useRef를이용해 Echart컴포넌트를 만들어 echarts.init 을 수행할 수 있는 dom엘리먼트에 접근한다.

시작

1단계 : Echarts 설치

npm install echarts

2단계 : Echarts 컴포넌트 생성

import React, { useEffect, useRef } from 'react';
import * as echarts from 'echarts';

type EchartProps = {
  chartCss: React.CSSProperties;
  chartOption: echarts.EChartsOption;
};

export default function Echart({ chartCss, chartOption }: EchartProps) {
  const chartRef = useRef(null);

  useEffect(() => {
    const chartInstance = echarts.init(chartRef.current); // ECharts 초기화 및 인스턴스 생성하기
    chartInstance.setOption(chartOption); // 차트 옵션 설정하기

		const resizeHandler = () => {
      chartInstance.resize(); // 차트 크기 조절
    };
    
		window.addEventListener('resize', resizeHandler);

    return () => {
      chartInstance.dispose();
      window.removeEventListener('resize', resizeHandler); // resize 이벤트 리스너 제거하기
    };
  }, [chartOption]); 
  
  return <div ref={chartRef} style={chartCss} />;
}

3단계 : 컴포넌트에서 Echarts 사용

export default function RankGraph({ teamRankData }: RankGraphProps) {
  const { leagueId } = useLeagueInfo({ season: '2024' });
  const { isPending, data: teamList, error } = useGetTeamList({ years: '2024', leagueId });

  **const chartCss: React.CSSProperties = {
    width: '100%',
    height: '100vh',
  };**

  if (isPending) {
    return <LoadingSpinner />;
  }

  if (error) {
    return <div>error</div>;
  }

  **const option = generateRankGraph(teamRankData, teamList);**

  **return <Echart chartCss={chartCss} chartOption={option} />;**
  • chartCss에서 가로, 세로 크기 정하기
  • generateRankGraph 는 echarts의 옵션을 생성하는 함수입니다.

4단계 : Echarts 옵션 생성하기

  • class 기반으로 옵션을 생성.
  • 데이터 처리와 차트 옵션 생성의 책임을 명확하게 분리할 수 있다.
  • 추가 로직에 유연하게 확장할 수 있습니다.
  • 차트 생성에 필요한 데이터를 클래스의 속성을 관리 할 수 있어 일관성 유지에 용이합니다.
  • 가독성에 용이합니다.
  • 다만 현재 코드는 특정기능(Rank Graph)에만 사용 가능하기에 유연하다고 보기는 어렵습니다.

0. constructor

constructor(teamRankData: TTeamRankInfo) {
    this.teamRankData = teamRankData;
    this.matchDayList = this.generateMatchDayList();
    this.rankingData = this.generateRankingData();
  }

1. 옵션 생성 generateOption

public generateOption(teamList: TTeamList): EChartsOption {
    return {
      title: { text: '팀 순위 그래프' }, // 그래프 타이틀
      tooltip: {
        // 호버시 띄울 tooltip
        trigger: 'item',
        formatter: function (params) {
          if (Array.isArray(params)) return ``;

          return makeTooltipHTMLWithLogo(
            params.seriesName as string,
            params.name,
            params.value as string | number,
            teamList,
          );
        },
      },
      grid: {
        left: 30,
        right: 200,
        bottom: 30,
        containLabel: true,
      },
      toolbox: {
        feature: {
          saveAsImage: {},
        },
      },
      xAxis: {
        // x축 데이터 생성
        type: 'category',
        splitLine: { show: true },
        axisLabel: {
          margin: 30,
          fontSize: 16,
        },
        boundaryGap: false,
        data: this.addedWeekMatchDayList,
      },
      yAxis: {
        type: 'value',
        axisLabel: {
          margin: 30,
          fontSize: 16,
          formatter: '{value}위',
        },
        inverse: true,
        interval: 1,
        min: 1,
        // max: Object.keys(this.rankingData).length,
        max: 10,
      },
      series: this.generateSeriesList(teamList),
    };
  }

2. Series 생성

private generateSeriesList(teamList: TTeamList): SeriesOption[] {
    const rich = this.makeRich(teamList);
    return Object.entries(this.rankingData).map(([name, data]) => ({
      name,
      symbolSize: 15,
      type: 'line',
      smooth: true,
      emphasis: { focus: 'series' },
      endLabel: {
        show: true,
        formatter: (params) => {
          const teamName = params.seriesName?.replace(reg, '');
          return `{${teamName}|} ${params.seriesName}`;
        },
        distance: 20,
        rich,
      },
      lineStyle: { width: 4 },
      data,
    }));
  }

3. 표로 보여줄 데이터 생성 rankingData

private generateRankingData(): Record<string, number[]> {
    const rankingData: Record<string, number[]> = {};

    this.matchDayList.forEach((matchDay, index) => {
      this.teamRankData.rankInfo[matchDay - 1].ranks.forEach((rank) => {
        if (!rankingData[rank.teamName]) {
          rankingData[rank.teamName] = [];
        }
        rankingData[rank.teamName][index] = rank.rank;
      });
    });

    return rankingData;
  }

4. xAxis data 생성

private generateMatchDayList(): number[] {
    const allMatchDays = this.teamRankData.rankInfo.map((info) => info.matchDay);
    const week = 5; // 보여줄 x갯수 = 5개 주

    // 최근 5주차 순위 그래프
    const start = Math.max(1, allMatchDays[allMatchDays.length - 1] + 1 - week);
    return allMatchDays.slice(start - 1);
  }

참고 자료

echarts example

echarts bump charts

react에서 echarts 사용

profile
마라토너

0개의 댓글