Financial Dashboard圖表

一開始在想chart類型和card類型的component要怎麼區分,邊看chart.js文件邊慢慢有點頭緒,可以把chart本身的設定,像是bar、line顏色相關的options寫在chart component,這樣就能達到標準化的目的 (引用同個component的長相會一致)

card除了chart component之外還包含其他要素,像是title、subtitle、等,分開處理比較單純

Chart.js

Chart.js是一個基於Canvas的函式庫,在繪製圖形之前先做好需要的設定,再把canvas畫上頁面

在React/Vite使用Chart.js

Chart.js + react-chartjs-2,兩個函式庫都要安裝

npm install chart.js react-chartjs-2

Chart component

react-chartjs-2 有做tree-shaking,註冊會用到的圖形以節省效能

import React from 'react';
import { Bar } from 'react-chartjs-2';
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend } from 'chart.js';

// Register Chart.js components
ChartJS.register(
  CategoryScale,
  LinearScale,
  BarElement,
  Title,
  Tooltip,
  Legend
);

function MyBarChart() {
  const data = {
    labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
    datasets: [{
      label: '# of Votes',
      data: [12, 19, 3, 5, 2, 3],
      backgroundColor: [
        'rgba(255, 99, 132, 0.2)',
        'rgba(54, 162, 235, 0.2)',
        'rgba(255, 206, 86, 0.2)',
        'rgba(75, 192, 192, 0.2)',
        'rgba(153, 102, 255, 0.2)',
        'rgba(255, 159, 64, 0.2)'
      ],
      borderColor: [
        'rgba(255, 99, 132, 1)',
        'rgba(54, 162, 235, 1)',
        'rgba(255, 206, 86, 1)',
        'rgba(75, 192, 192, 1)',
        'rgba(153, 102, 255, 1)',
        'rgba(255, 159, 64, 1)'
      ],
      borderWidth: 1
    }]
  };

  return <Bar data={data} />;
}

export default MyBarChart;

常用設定

color, borderColor, backgroundColor, label, title, legend label和legend有分為chart層級和graph層級

const data = {
  labels: ['A', 'B', 'C'],
  datasets: [
    {
      label: 'Dataset 1',
      data: [1, 2, 3],
      borderColor: '#36A2EB',
      backgroundColor: '#9BD0F5',
    },
    {
      label: 'Dataset 2',
      data: [2, 3, 4],
      borderColor: '#FF6384',
      backgroundColor: '#FFB1C1',
    }
  ]
};

動態資料呈現:useContext + SWR

如果每個圖表都各自去打 API,會很難統一資料的來源與更新頻率。可以用 SWR 來處理資料請求,再用 React context 把資料傳下去,讓不同的圖表共用同一份資料。

context 的好處是資料載入一次就好,每個元件可以直接取用,不用重複發出request。

建立 context 和 provider

// context/DataContext.js
import React, { createContext } from 'react';
import useSWR from 'swr';

export const DataContext = createContext();

const fetcher = (url) => fetch(url).then((res) => res.json());

export function DataProvider({ children }) {
  const { data, error, isLoading } = useSWR('/api/stock', fetcher);

  return (
    <DataContext.Provider value={{ data, error, isLoading }}>
      {children}
    </DataContext.Provider>
  );
}

在 App 加上 provider

import { DataProvider } from './context/DataContext';
import MyDashboard from './components/MyDashboard';

function App() {
  return (
    <DataProvider>
      <MyDashboard />
    </DataProvider>
  );
}

子元件中使用資料

import { useContext } from 'react';
import { DataContext } from '../context/DataContext';

function MyChartCard() {
  const { data, isLoading, error } = useContext(DataContext);

  if (isLoading) return <div>載入中...</div>;
  if (error) return <div>資料錯誤</div>;

  const chartData = transformDataForChart(data);

  return <Bar data={chartData} />;
}

這樣做就能讓不同圖表元件共用一份 SWR 資料來源,也方便之後加上 refresh 或 cache 邏輯。