Highcharts|Highcharts React Wrapper Dashboard

highcharts logo




在本教學中,我將向你展示如何用Highcharts和React包裝器創建一個簡單的、交互式的儀錶板。這篇文章適用於各個層次的開發者。對於任何有經驗的開發者來說,它都可以快速閱讀,並為那些剛接觸React或Highcharts的人提供幫助。

備註

我使用Create React App和Visual Studio Code IDE來構建這個項目;你可以從這個Github鏈接或Codesandbox上下載這個項目。如果需要,可以參考之前的文章,了解如何從頭創建一個項目。

下面的儀錶盤直觀地顯示了2001年至2015年美國的淨能源生產情況。該儀表板允許瀏覽者比較以下類別的主要能源生產情況:化石燃料、水電能源、生物質能和可再生能源。我從美國能源信息署(又稱EIA)收集了這些數據。

在創建儀表板時,我遵循了這些最佳做法。

  1. 將背景與圖表分開(即用顏色來標記每個圖表部件的邊界)。
  2. 保持圖例與圖表接近
  3. 避免擁擠的圖表(即把重要的信息分成多個圖表,而不是把太多的數據塞進一個圖表中
  4. 提供探索數據的工具(如縮放和過濾)
  5. 考慮響應式設計

要了解更多關於如何創建一個有效的儀錶盤,請查看以下文章:在將圖表放入儀錶盤之前需要考慮的6件事;在那裡你會發現關於上述每一點的更多細節和演示。

要檢查實時互動儀表板,請點擊Codesandbox項目鏈接,或點擊下面的圖片。


讓我們來看看創建儀表板的過程吧!

架構

讓我們從回顧全局架構開始。架構規劃有助於顯示組件之間的連接;它還能使數據流可視化。我強烈建議在跳轉到編碼之前花一些時間規劃你的架構;你將節省大量的時間和頭髮的拉扯。

這個儀錶盤的主要想法相對簡單。用戶通過用戶界面選擇日期範圍,這個動作觸發了從這個日期範圍提取數據的過程,按能源來源總結數據,然後將結果歸類為化石燃料、水電能源、生物質能或可再生能源。最後一步是將數據顯示為四個互動的多納特圖,每個類別一個。順便說一下,甜甜圈圖或餅圖是顯示比較和構成的最佳方式;它們結構緊湊,易於理解;這就是為什麼它們經常被用於儀錶盤。

我的下一個合乎邏輯的行動是將這個想法分解成小的組件,以使我更容易編碼和維護每個組件。經過幾次草圖設計和優化,我最終得到了七個組件。

  1. 數據組件:保存從EIA網站收集的數據。
  2. 數據處理。為了在用戶選擇後將數據加起來。
  3. 圖表模板。設置Highcharts庫所要求的交互式圖表的配置。
  4. 範圍選擇組件。處理用戶使用的日期選擇器。
  5. 圖表模塊。在用戶選擇的每個範圍後渲染圖表。
  6. 儀表板組件。在一個容器中收集和呈現所有的聊天記錄。
  7. 應用程序組件。收集所有的組件並運行儀表板。

然後,我把我的項目分為七個組件,四個ReactJS組件(紫色)和三個模塊(綠色)。我的標準很簡單,如果有任何東西需要渲染,該組件就成為ReactJS組件,否則,它只是一個標準的javascript模塊,沒有渲染,只有一堆對像或函數用於數據處理。


我不得不將Highcharts React Wrapper(藍色)添加到架構中,以實現可視化,並知道我必須將該庫添加到我的項目中。由於圖表的渲染將在Chart組件中進行,我將Highcharts React Wrapper導入到該組件中。

Coding...有趣的部分來了

設置好架構後,我用Create React App創建了一個新的項目;然後我添加了組件文件夾,在那裡我為每個組件製作了一個文件。為了將ReactJS組件與其他模塊分開,我將每個ReactJS組件文件的第一個字母以大寫字母開始;這是一個很好的做法,可以快速識別哪個文件做什麼,因為它使導入直觀,並減少文件之間的任何歧義。

請看下面的項目文件結構。



讓我們深入探討每個組成部分。

Data

數據模塊有一個對像數據,我把每個能源的數據存儲在一個專門的對像中,然後我使用導出默認數據導出對像數據。

const data = {
 coal: {
   2001: 1903955,
...
 },
 pliquids: {
   2001: 114646,
...
 },
 pcoke: {
   2001: 10235,
...
 },
 ngas: {
   2001: 639129,
...
 },
 ogas: {
   2001: 9040,
...
 },
 nuclear: {
   2001: 768825,
...
 },
 chydroelectric: {
   2001: 216962,
...
 },
 wind: {
   2001: 6737,
...,
   2015: 93876
 },
 solar: {
   2001: 543,
...
 },
 geothermal: {
   2001: 13740,
...
 },
 biomass: {
   2001: 49749,
 ...
 },
 wood: {
   2001: 35199,
...
 },
 otherbio: {
   2001: 14549,
...
 }
};

export default data;

DATA PROCESSING

該模塊從數據模塊導入數據,然後將數據與yearFrom和yearTo作為參數傳遞給箭頭函數dataProcessing。

import data from './data';
let dataProcessing = (yearFrom, yearTo) => {
...

該函數計算出每個能源在日期範圍內產生的能量數量。

if (yearFrom < yearTo) {
   let coal = 0,
     pliquids = 0,
     pcoke = 0,
     ngas = 0,
     ogas = 0,
     nuclear = 0,
     chydroelectric = 0,
     wind = 0,
     solar = 0,
     geothermal = 0,
     biomass = 0,
     wood = 0,
     otherbio = 0;
   for (let i = yearFrom; i < yearTo; i++) {
     coal += data.coal[i];
     pliquids += data.pliquids[i];
     pcoke += data.pcoke[i];
     ngas += data.ngas[i];
     ogas += data.ogas[i];
     nuclear += data.nuclear[i];
     chydroelectric += data.chydroelectric[i];
     wind += data.wind[i];
     solar += data.solar[i];
     geothermal += data.geothermal[i];
     biomass += data.biomass[i];
     wood += data.wood[i];
     otherbio += data.otherbio[i];
   }

根據能源的性質,計算出的數據被保存在四個對像中。

 fossilFuelData = [
     { name: 'coal', y: coal },
     { name: 'Petroleum Liquids', y: pliquids },
     { name: 'Petroleum Coke', y: pcoke },
     { name: 'Natural gas', y: ngas },
     { name: 'Other Gases', y: ogas }
   ];

   hydroElectricData = [
     { name: 'Nuclear', y: nuclear },
     { name: 'Conventional Hydroelectric', y: chydroelectric }
   ];

   biomassData = [
     { name: 'Biomass', y: biomass },
     { name: 'Wood', y: wood },
     { name: 'Otherbio', y: otherbio }
   ];

   renewableEnergyData = [
     { name: 'Wind', y: wind },
     { name: 'Solar', y: solar },
     { name: 'Geothermal', y: geothermal }
   ];

該函數還處理了一個信息,以表示給用戶選擇範圍,並確保yearFrom總是小於或等於yearTo。


Template

儀錶盤有四個交互式甜甜圈圖表,所以我沒有單獨創建每個圖表,而是按照Highcharts庫的結構設置了一個模板模塊,然後在App模塊中使用它來創建圖表。

const template = {
 userConfig: {
   tooltip: {
     pointFormat: "{point.y} thousand megawatthours"
   },
   plotOptions: {
     pie: {
       showInLegend: true,
       innerSize: "60%",
       dataLabels: {
         enabled: false,
         distance: -14,
         color: "white",
         style: {
           fontweight: "bold",
           fontsize: 50
         }
       }
     }
   }
 },
 yearFrom: "2001",
 yearTo: "2015",
 msg: "Select the range"
};

export default template;

正如你所看到的,該模板有主要的標準Highchart圖表配置,如工具提示和plotOptions。

SELECTION

這個文件是一個ReactJS組件;它渲染了選擇元素,允許用戶選擇日期範圍。


對於這個組件,我使用Bootstrap來設計元素的樣式,你可以自由地想出你自己的樣式,但同樣為了可維護性,我決定使用一個標準的樣式庫。

Charts

Chart.js文件是另一個ReactJS組件;它在每次有更新時都會渲染圖表。

這個組件的關鍵元素是componentDidUpdate(prevProps, prevState)方法;我用它來檢查是否有任何更新,然後獲取新圖表的參數,再將這些參數傳遞給Chart狀態組件。

componentDidUpdate(prevProps, prevState) {
    if (this.props.data !== prevProps.data) {
      this.setState({
        chartData: {
          ...this.state.chartData,
          subtitle: {
            text:
              (
                this.props.data.reduce((accumulator, obj) => accumulator + obj.y,0) / 1000).toFixed(2) + " TWh"
          },
          series: [
            {
              data: this.props.data
            }
          ]
        }
      });
    }
  }

DASHBOARD

在這個組件中,我使用以下代碼動態地渲染所有的圖表。

{this.props.charts && this.props.charts.map(chart => {
           return (
             <div className="col-xs-12 col-sm-6 mb-2" key={i}>
               <Chart
                 data={chart.serie}
                 userConfig={this.props.userConfig}
                 titleName={chart.title}
               />
             </div>
           );
         })}

APP

最後,我把所有的ReactJS組件聚集在這個主組件App.js中,以渲染整個儀表板。

在這個組件中,有一個箭頭功能 handleChangeYear。

handleChangeYear = e => {
              this.setState({
                  [e.target.name]: e.target.value
              });
};

該函數處理選擇元素的事件,並將新的時間範圍保存在組件App.js的狀態中;這個過程會觸發方法componentDidUpdate(prevProps, prevState)。

 componentDidUpdate(prevProps, prevState) {
   if (prevState.yearFrom !== this.state.yearFrom) {
     this.handleChangeSelect();
   }
   if (prevState.yearTo !== this.state.yearTo) {
     this.handleChangeSelect();
   }
 }

該組件componentDidUpdate(prevProps, prevState)調用handleChangeSelect()來處理數據(在數據處理模塊),然後更新圖表。

整個過程的最後一步是在儀表板上顯示新的圖表。

在App.js文件中,我還使用了Bootstrap來設計儀錶盤上顯示的不同元素的樣式。

項目完成後,我意識到最具挑戰性和最耗時的部分是弄清架構;之後,編碼部分就很簡單了。我在handleChangeYear方面也遇到了一些挑戰;我不得不使用括號符號來確定我正在處理正確的按鈕。

延伸閱讀
aa71435723的大頭照
Winston

Eggplant DAI 自動化測試專家。

留言