iT邦幫忙

2021 iThome 鐵人賽

DAY 23
0
Modern Web

用React刻自己的投資Dashboard系列 第 23

用React刻自己的投資Dashboard Day23 - 非同步呼叫API,完成首頁資料串接

tags: 2021鐵人賽 React

上一篇確認過API內容之後,剩下的部份就是串接API,並將資料呈現在畫面上了,雖然描述起來是短短兩句,不過還是寫了好多的程式碼,下面就會挑兩個重點部份來說明。

非同步呼叫API,增進程式效能

在取得美國股市不同指數的收盤資料時,因為參數不同,每個指數都需要呼叫一次API,下面比較我剛開始的寫法跟後來非同步的寫法,速度差蠻多的。

  • 剛開始的寫法:一支回傳後再叫下一支
    這邊說明一下,資料起始日的部份,我是從當天往前推4天開始都收,主要原因是為了怕撈到假日,因為美檔指數都要有最近兩日的資料,因為美國休市日都大概1~2天,因此4天應該是夠用。
const fetchCloseData = async () => {
  # 指定資料起始日
  let parse_date = new Date(today.valueOf() - 4 * 1000 * 60 * 60 * 24).toISOString().slice(0, 10);
  
  # 一支API跑完再跑下一支
  const DJI = await FinmindAPIUS({ dataset, data_id: "^DJI", start_date: parse_date, end_date: "" })
  const GSPC = await FinmindAPIUS({ dataset, data_id: "^GSPC", start_date: parse_date, end_date: "" })
  const IXIC = await FinmindAPIUS({ dataset, data_id: "^IXIC", start_date: parse_date, end_date: "" })
  const SOX = await FinmindAPIUS({ dataset, data_id: "^SOX", start_date: parse_date, end_date: "" })
  const VIX = await FinmindAPIUS({ dataset, data_id: "^VIX", start_date: parse_date, end_date: "" })

  return ...
  
};

  • 改良後的寫法:非同步執行
    這樣的寫法讓程式一開始執行的時候,5支API請求會一起發送,而Promise.all則是會等5支API都有回應之後,才算完成,因此這樣的程式一方面可以確保資料都有拿到,另一方面因為不用等前面的API回應後才執行下一支,速度會有所提升。
const fetchCloseData = async () => {
  # 指定資料起始日
  let parse_date = new Date(today.valueOf() - 4 * 1000 * 60 * 60 * 24).toISOString().slice(0, 10);
  
  # 非同步執行
  const [DJI, GSPC, IXIC, SOX, VIX] = await Promise.all([
    FinmindAPIUS({ dataset, data_id: "^DJI", start_date: parse_date, end_date: "" }),
    FinmindAPIUS({ dataset, data_id: "^GSPC", start_date: parse_date, end_date: "" }),
    FinmindAPIUS({ dataset, data_id: "^IXIC", start_date: parse_date, end_date: "" }),
    FinmindAPIUS({ dataset, data_id: "^SOX", start_date: parse_date, end_date: "" }),
    FinmindAPIUS({ dataset, data_id: "^VIX", start_date: parse_date, end_date: "" }),
  ])

  return ...
  
};

根據資料需求變更版面

如下圖這兩個資料,之前有提到單一資料區塊已經有拉一個共用的UI模板出來,所以這張圖可以看成兩個類似的UI模板,不過其實又有些許的不同,加權指數那條有三個資料,而買賣超只有一個資料,但是我還是想讓他們共用一個UI就好,因為長相是類似的。

作法就是當收到的資料有包含spread,就表示它是上面那個樣子的模板,如果沒有,就是下面的模板,因此程式碼撰寫如下,透過判斷props傳進來的資料有哪些,去return適合的版型,不過看起來JSX的部份還是蠻冗長的就是了,時間有限,就先這樣子寫,有時間的話應該可以切更多子件,看起來會更簡潔一些。

src\UI\IndexCloseInfo\IndexCloseInfo.js

import React from 'react';
import styles from './IndexCloseInfo.module.css';
import { Col } from 'react-bootstrap';

const Card = (props) => {
  # 有spread資料
  if (props.data && props.data.close.spread) {
    return (
      <Col sm={12} md={6} lg={4} xl={3} xxl={3} className={styles.bar}>
        <div className={styles.bar_left_side}>
          <div className={styles.bar_icon}>{props.data.info.iconText}</div>
        </div>
        <div className={styles.bar_right_side}>
          <p className={styles.bar_text}>{props.data.info.name}</p>
          <div className={styles.bar_data_group}>
            <p className={`${styles.bar_data} ${props.data.close.spread > 0 && props.data.close.spread !== 0 ? styles.red_text : styles.green_text}`}>{props.data.close.price}</p>
            <p className={styles.bar_data}>|</p>
            <p className={`${styles.bar_data} ${props.data.close.spread > 0 && props.data.close.spread !== 0 ? styles.red_text : styles.green_text}`}>{props.data.close.spread}</p>
            <p className={styles.bar_data}>|</p>
            <p className={`${styles.bar_data} ${props.data.close.spread > 0 && props.data.close.spread !== 0 ? styles.red_text : styles.green_text}`}>
              {
                Math.round(props.data.close.spread * 10000 / (props.data.close.price - props.data.close.spread)) / 100
              }
              %
            </p>
          </div>
        </div>
      </Col >
    )
    # 沒有spread資料
  } else if (props.data && !props.data.close.spread) {
    return (
      <Col sm={12} md={6} lg={4} xl={3} xxl={3} className={styles.bar}>
        <div className={styles.bar_left_side}>
          <div className={styles.bar_icon}>{props.data.info.iconText}</div>
        </div>
        <div className={styles.bar_right_side}>
          <p className={styles.bar_text}>{props.data.info.name}</p>
          <div className={styles.bar_data_group}>
            <p className={`${styles.bar_data} ${props.data.close.price > 0 && props.data.close.price !== 0 ? styles.red_text : styles.green_text}`}>{+(Math.round(props.data.close.price / 100000000 + "e+2") + "e-2")}億</p>
          </div>
        </div>
      </Col >
    )
    # 沒有任何資料
  } else {
    return <p>No data</p>
  }
};

export default Card;

小結

其實剛開始寫的時候,沒有想到首頁的程式碼會有這麼多,有一部分的原因可能是因為無法改變API的內容,所以需要寫一些整理資料的程式,雖然畫面處理的速度可能有點慢,不過還是成功串接了API,並隨時顯示最新資料,蠻有成就感的。


上一篇
用React刻自己的投資Dashboard Day22 - API與前端資料需求比對
下一篇
用React刻自己的投資Dashboard Day24 - styled components
系列文
用React刻自己的投資Dashboard30

尚未有邦友留言

立即登入留言