iT邦幫忙

2022 iThome 鐵人賽

DAY 8
0
自我挑戰組

SPYxFRONTEND ~ 懂一點後端真是讓人哇哭哇哭系列 第 8 篇

[Day8] 用 fetch 拿資料好愜意

  • 分享至 

  • xImage
  •  

前言

因為昨天文章發的有點匆忙,
今天的文章會再補充一下昨天介紹的 XHR,
然後練習 fetch 的用法~

本日正文

用 XHR 好像有點麻煩?

昨天練習了用 XHR 拿資料的方法,
但是有沒有覺得它用起來有點麻煩?
前面都要先宣告 XMLHttpRequest 的物件,
然後還得寫 open 跟 send 才會真的發送請求出去:

const xhr = new XMLHttpRequest();
  xhr.open("get", sourceUrl, true);
  xhr.send(null);

再來好不容易發送請求出去了,
你還沒辦法直接取用 call API 後回傳的資料,
還得再多寫 xhr.onload,
這時候才拿得到資料才能做後續的處理:

xhr.onload = function () {
		setdataResult(JSON.parse(xhr.responseText));
	};
}

而且如果是直接在 xhr.onload 裡面寫變數,
因為是在 function 內,
function 外沒辦法直接取得這個變數(作用域的關係),
例如這邊將昨天的例子改寫成這樣:

function fetchData(){
    const xhr = new XMLHttpRequest();
    xhr.open("get", sourceUrl, true);
    xhr.send(null);
    xhr.onload = function () {
      result = JSON.parse(xhr.responseText);
      console.log(result);
    };
    console.log(result);
  }

然後文字的地方改成直接取用 {result}

<Text my={4} fontSize="p" textAlign="center">
  result: {result?.records?.location?.length}
</Text>

https://ithelp.ithome.com.tw/upload/images/20220909/20129873jO8ggzphwp.png

這樣在 xhr.onload 外面其實是拿不到資料的(要另外處理),
所以昨天後來我是用 useState 去改值(將拿到的資料設定進去),
這樣是不是有夠麻煩orz

因為 XMLHttpRequest 算是很陽春的 AJAX 實作方法,
所以之後也出現了幾個好用的工具,
我覺得應該是為了克服 XHR 的一些缺點啦。

好用的 fetch

像是今天要練習的 fetch 就是其中一個,
而 fetch 是使用 Promise 物件的寫法,
那 Promise 又是什麼呢?

Promise 要認真說的話又可以獨立一篇了XD
這邊推薦可以看大大的文章 → JavaScript Promise 全介紹

我自己簡單的理解就是,
Promise 是為了優化 Javascript 的 非同步 語法部份,
例如在寫 Javascript 時你會發現你寫在前後行的語法(EX: 執行A; 執行B;),
但並不會真的照你寫的順序執行,
而是"同時"執行,
所以如果你需要寫 A執行完才要執行B 的語法,
Promise 就能解決這個問題~

前情提要講完了,
那我們接下來就看一下 fetch 的簡單寫法吧:

function fetchData(){
	fetch(sourceUrl)
	.then(function(response) {
	  return response.json();
	})
	.then(function(myJson) {
	  console.log(myJson);
	});
}

簡單說明一下,
fetch(sourceUrl) 這邊意思就是傳送要求到 sourceUrl,
等到拿到 response 之後(.then) 回傳 response.json(),
接著(.then) 再用 console.log 把 response.json() 印出來,
像這樣:
https://ithelp.ithome.com.tw/upload/images/20220909/20129873wx8VKePWDm.png

不需要特別寫 open, send, onload 就能完成 call API 並拿到回傳資料,
這樣是不是簡單多了呢?

既然 fetch 拿資料這麼方便,
那我們今天用拿到的資料來簡單寫一下應用好了。

首先在動手前,昨天也有提到我會習慣先觀察一下資料:
https://opendata.cwb.gov.tw/api/v1/rest/datastore/F-C0032-001?Authorization=rdec-key-123-45678-011121314

https://ithelp.ithome.com.tw/upload/images/20220909/20129873I553oqf6lQ.png

題外話,你可能會發現資料長得很密密麻麻要怎麼看?
這邊推薦可以裝 Chrome 的擴充套件 JSON Viewer,
然後它就會把 JSON 轉換成比較可讀的樣子,
像這樣:
https://ithelp.ithome.com.tw/upload/images/20220909/20129873tiB1zxrQ0j.png

從資料裡面可以看到有各縣市的明日天氣預報資料,
現在假設我明天要去臺中玩(並沒有T_T),
我想關心一下臺中明天的天氣情況,
這邊就稍微處理一下拿到的資料,
我習慣是觀察好資料結構後,先拿一筆資料試寫看看(這邊容我這邊快轉一下):

function App() {
  const sourceUrl =
    "https://opendata.cwb.gov.tw/api/v1/rest/datastore/F-C0032-001?Authorization=rdec-key-123-45678-011121314";
  const [dataResult, setdataResult] = useState([]);
  const searchArea = '臺中市';
  const [dataSearchArea, setDataSearchArea] = useState({});
  let result = [];

  function fetchData(){
    fetch(sourceUrl)
    .then(function(response) {
      return response.json();
    })
    .then(function(myJson) {
      setdataResult(myJson.records.location);
    });
  }

  useEffect(() => {
    setDataSearchArea(dataResult.filter((d) => d.locationName === searchArea));
  }, [dataResult]);

  return (
    <ChakraProvider theme={theme}>
      <>
        <Text my={4} fontSize="p" textAlign="center">
          sourceUrl: {sourceUrl}
        </Text>
        <Text my={4} as="h2" fontSize="2xl" textAlign="center">
          全部總共 {dataResult.length === 0 ? 0 : dataResult?.length } 筆資料
        </Text>
        <Divider my={4} />
        <Text my={4} as="h3" fontSize="xl" textAlign="center">
          區域: {searchArea}
        </Text>
        <Text px={4}>
          [明日天氣預報]
          <br />
          時間:{dataSearchArea[0]?.weatherElement[0]?.time[0]?.startTime}
          {' ~ '}
          {dataSearchArea[0]?.weatherElement[0]?.time[0]?.endTime}
          <br />
          天氣:{dataSearchArea[0]?.weatherElement[0]?.time[0]?.parameter?.parameterName}
        </Text>
        <Divider my={4} />
        <Center>
          <Button onClick={fetchData}>取得資料</Button>
        </Center>
      </>
    </ChakraProvider>
  );
}

https://ithelp.ithome.com.tw/upload/images/20220909/20129873rcZBxUbSWj.png

然後從資料我們可以看到每天的預報資料有 3 筆 (在 time 裡面),
https://ithelp.ithome.com.tw/upload/images/20220909/20129873lEoSvIv0oc.png
所以先用一筆確定是你要的畫面之後,
我就會把 time 用 map 以迴圈的方式讀取,把 3 筆資料都渲染出來,
像這樣:

taSearchArea[0]?.weatherElement[0]?.time?.map((t) => (
    <Box>
      時間:{t.startTime}
      {' ~ '}
      {t.endTime}
      <br />
      天氣:{t.parameter?.parameterName}
    </Box>
  ))}

https://ithelp.ithome.com.tw/upload/images/20220909/20129873xKoCLZkXnu.png

這樣你就可以看到臺中市的明天天氣預報資料都出現了~
(看起來不會下雨真是太好了但我不會去臺中玩QQ)

如果去 fetch 遇到 error 怎麼辦?例外處理很重要

到這邊千萬不要高興太早,
如果你去 fetch 遇到 error (EX. 目的地server掛掉,或網址打錯...等)
那會怎樣呢?
這邊我故意把 url 多打字來試試看:
https://ithelp.ithome.com.tw/upload/images/20220909/20129873gNxDkSLiN4.png

你可以看到畫面上都沒有資料,
打開 console 來看可以看到有 error
(PS. 其實一般來說,應該是瀏覽器會直接跳 error 畫面,
所以 error 是需要另外處理的,
但可能我裝的套件有特別處理這個事件,所以畫面上很安靜什麼都沒發生)

這種情況該怎麼辦呢?
Promise 有提供 catch 的語法,
讓你設定在執行前面動作遇到 error 會執行什麼動作,
像這樣:

fetch(sourceUrl)
    .then(function(response) {
      return response.json();
    })
    .then(function(myJson) {
      setdataResult(myJson.records.location);
    })
    .catch((err) => {
      alert("抓取資料錯誤,請確認後再試");
      console.log(err);
    })

https://ithelp.ithome.com.tw/upload/images/20220909/20129873AlM7gDK0aB.png

這邊你就會看到跳出一個警告視窗顯示:抓取資料錯誤,請確認後再試,
而這個就是 Exception Handling(例外處理),
顧名思義,就是在程式未正常執行時要做什麼處理,
尤其是在跟後端介接 call API 時,例外處理就會變得很重要,
要依據後端給出的 error code 不同時要做不同的處理,
像是前端顯示不同的錯誤訊息,
讓使用者或工程師看到很快知道要怎麼處理這樣~

因此後面的篇章 catch 會常常出現,
也是我們在 call 後端 API 時一定要寫的語法,
今天介紹完 fetch 明天就讓我們進入 axios 的世界吧~~~

參考文章:

request的方式? ajax & fetch & axios

後記

今天終於比較沒那麼趕了orz
感覺好像每年中秋連假都在寫鐵人賽文章中度過XD
真充實XDDDD


上一篇
[Day7] 來到 call API 的第一道門 ~ AJAX - XMLHttpRequest 篇
下一篇
[Day9] 來認識 axios 吧!之「既生 fetch,何生 axios?」
系列文
SPYxFRONTEND ~ 懂一點後端真是讓人哇哭哇哭 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言