文章分類/Highcharts
在本教學中,我將向你展示如何用Highcharts和React包裝器創建一個簡單的、交互式的儀錶板。這篇文章適用於各個層次的開發者。對於任何有經驗的開發者來說,它都可以快速閱讀,並為那些剛接觸React或Highcharts的人提供幫助。
備註
我使用Create React App和Visual Studio Code IDE來構建這個項目;你可以從這個Github鏈接或Codesandbox上下載這個項目。如果需要,可以參考之前的文章,了解如何從頭創建一個項目。
下面的儀錶盤直觀地顯示了2001年至2015年美國的淨能源生產情況。該儀表板允許瀏覽者比較以下類別的主要能源生產情況:化石燃料、水電能源、生物質能和可再生能源。我從美國能源信息署(又稱EIA)收集了這些數據。
在創建儀表板時,我遵循了這些最佳做法。
要了解更多關於如何創建一個有效的儀錶盤,請查看以下文章:在將圖表放入儀錶盤之前需要考慮的6件事;在那裡你會發現關於上述每一點的更多細節和演示。
要檢查實時互動儀表板,請點擊Codesandbox項目鏈接,或點擊下面的圖片。
讓我們來看看創建儀表板的過程吧!
架構
讓我們從回顧全局架構開始。架構規劃有助於顯示組件之間的連接;它還能使數據流可視化。我強烈建議在跳轉到編碼之前花一些時間規劃你的架構;你將節省大量的時間和頭髮的拉扯。
這個儀錶盤的主要想法相對簡單。用戶通過用戶界面選擇日期範圍,這個動作觸發了從這個日期範圍提取數據的過程,按能源來源總結數據,然後將結果歸類為化石燃料、水電能源、生物質能或可再生能源。最後一步是將數據顯示為四個互動的多納特圖,每個類別一個。順便說一下,甜甜圈圖或餅圖是顯示比較和構成的最佳方式;它們結構緊湊,易於理解;這就是為什麼它們經常被用於儀錶盤。
我的下一個合乎邏輯的行動是將這個想法分解成小的組件,以使我更容易編碼和維護每個組件。經過幾次草圖設計和優化,我最終得到了七個組件。
然後,我把我的項目分為七個組件,四個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方面也遇到了一些挑戰;我不得不使用括號符號來確定我正在處理正確的按鈕。