📇Index:
Basic Data Loading
Fast Navigation
Dependent Fetching
Conditional Fetching
Multiple Arguments
Refetch On Interval
Suspense
Local Mutate
前言
SWR is a React Hooks library for remote data fetching.
身為一個使用 React 的開發者一定對於使用 fetch 或是 axios 發出 API 請求資料不陌生,但是你也一定會遇到以下問題:
- 在元件重新載入時,因為沒有 cache,所以資料必須重新請求 API 一次。
- 想要實現非同步渲染。
- 在切換瀏覽器的分頁時,有些驗證必須在重新整理瀏覽器後,才能讓 React 再重新驗證一次。
- 一個 API 必須等待另一個 API 完成後才能開始執行。
SWR 是由開發 Next.js 的團隊成員在 2019/10/29 開源的專案,在 1 個月左右的時間就拿到 5000 顆星星,是一個眾所矚目的開源專案。接下來,會用幾個範例帶大家了解 SWR 如何使用,以及它強大的效果。
Basic Data Loading
👉 按這裡看程式碼
SWR 的起手式非常簡單,在以上的範例中 useSWR
帶有 2 個參數:
- 第 1 個參數為
key
:key
是一個字串,作為資料的來源。 - 第 2 個參數為
fetcher
:fetcher
被用來傳入如何取得資料的函式,例如: 請求 API 經常使用的 fetch 或是 axios,將它們包裝成函式傳入。
範例中 SWR 的回傳值有 data
與 error
,它們分別為 fetcher
的回傳結果,以及從 fetcher
被丟出的錯誤。
在資料尚未載入時,data
都是回傳 undefined
,因此,我們就可以利用 data
的回傳值判斷要不要渲染資料,用這種方式實現非同步渲染的效果。
Fast Navigation
如果一開始 API 已經成功回傳資料,並成功建立 cache,在元件切換時,SWR 能夠快速地從 cache 中讀取需要的資料。
而且請求 API 程式碼與前一個「Basic Data Loading」的範例一模一樣,不用修改任何的程式碼就可以達成。
👉 按這裡看程式碼
Dependent Fetching
👉 按這裡看程式碼
我們在使用 REST API 時很常會遇到這樣的情況,有一個 API 必須等待另一個 API 的回傳值才能發出請求。以往我們在這種情況下也許會使用 then
、Promise
、 await/async
,或是使用套件 react-observable、react-saga 等,以上幾種方法撰寫額外的程式碼,才能讓非同步的請求可以相互等待。
SWR 能夠幫助我們輕易的解決 API 依賴的問題,像是我們想要請求一位 user 的 posts,只要把請求 user 的回傳值放到請求 posts 的 key
中,SWR 就會在拿到 user 的資料後才發出請求,讀取這位使用者的 posts。
Conditional Fetching
👉 按這裡看程式碼
有時候,我們希望 API 的請求是能夠由使用者自己掌握的,而不是在元件載入時就自動發出請求。像是常見我們在購物網站看到「查看更多」的按鈕,在點選按鈕後才載入更多的商品資訊。
要使用 SWR 達成這件事也很簡單,上面的範例,在元件載入時 fetchSwitch
為 false
,讓 useSWR
的 key
是 null
,因此不會第一時間就請求 API。在使用者點擊按鈕後fetchSwitch
變成 true
,useSWR
才成功獲得 key
並請求 API 。
Multiple Arguments
👉 按這裡看程式碼
我們平時使用 API 時,不一定只會用到一個參數,像是 GET 時,我們也許會帶一些額外的訊息,告知我們要的是 id 為 20 的 post;一些有權限限制的 API,也會要求 Header 必須帶有 Token。
❌ 然而,要注意的是在使用 SWR 時,不要這樣做:
useSWR('/api/data', url => fetchPost(url, postId))
原因是,SWR 決定要不要 refetch 取決於第一個參數 key
有沒有改變,如果像是上述這樣使用,儘管 postId
變動了,key
仍然是 /api/data,因此,useSWR
的回傳值就會是錯誤的。
👌 正常的使用方式是將 key
變成 array,把 postId
放 array 中:
useSWR(['/api/data', postId], fetchPost)
如此一來, 當 postId
改變時,SWR 就可以成功被通知並發送 API 的請求。
你以為這樣就沒問題了嗎 ?
因為 SWR 用的是 shallow compare,當參數的型態是 Object 時,比較的是 reference。因此,在每次元件渲染時,reference 都會被重新分配,SWR 會誤以為 key
改變了,會再次發送一次請求。
將 Object 直接帶入 key
中,會讓 React 直接炸開,跑出以下的錯誤訊息:
Uncaught Invariant Violation: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
Using useMemo to solve this problem
👉 按這裡看程式碼
在使用多個參數時,如果參數的型態是 Object,我們必須使用 React Hooks 中經常使用的 useMemo
將 id
記錄起來。下次在重新渲染時,useMemo
發現如果 id
的數值沒有變動,將不會重新分配新的記憶體給 params
,就不會引發 React 無窮渲染的問題。
Refetch On Interval
👉 按這裡看程式碼
在有些情況下,我們希望在頁面上的資訊可以定時自動更新,使用者不用重新整理網頁就可以看到新的資料。
SWR 提供了 useSWR
的第 3 個參數讓我們可以設定請求 API 的頻率,以上範例中每 0.5 秒就會請求 API 一次,在完成後也會同時觸發元件的重新渲染。
第 3 個參數是一個物件,有許多額外的選項可以設定,詳情請洽 github。
這種方法也可以用在多個視窗上,如此一來,在 POST 一個新的資料後,另一個視窗不用重新整理也能夠定時的獲取最新的資料。
Suspense
👉 按這裡看程式碼
React 在 16.6 就提供了非同步渲染 Suspense 的解決方案,但是目前仍然是實驗性質的方法,不建議使用在產品上面。
SWR 仍然提供了介面讓我們可以使用 Suspense,而且使用上也非常簡單,只要在 useSWR
的第 3 個參數上加上 suspense: true
,就可以在請求 API 時使用 Suspense。
第 3 個參數是一個物件,有許多額外的選項可以設定,詳情請洽 github。
Local Mutate
👉 按這裡看程式碼
Local mutate 是一個有點特殊的功能,不知道你有沒有遇過一種情況,在發出 API 的請求後,你不想等 API 回傳的資料才更新頁面,因為回傳的資料是可預期的資料,所以可以像是以上的範例,不等回傳,而是直接改變 data
的值。
不過因為這個功能直接使用官方的範例程式會發一些問題,在筆者發出 ISSUE後,根據開發人員的回覆必須加上
dedupingInterval
的設定才可以動 ✋。That’s because of deduping and the focus and mutate events happened too quickly so the revalidation got ignored. If you set
dedupingInterval: 0
then it will be fine.
結論
SWR 作為 React Hooks 請求資料的一種解決方案,看起來是強大且易用的。它提供了資料的 cache,以及讓 API 依賴請求更容易撰寫,甚至提供了 Suspense mode。
😅不過目前官方主要推廣的其中一個 feature — revalidate 有發現一些問題,根據 SWR 開發人員的說法「事件的監聽會發生 race condition」,現在正在等待修復中。
分享就到這邊,如果喜歡我的文章可以幫我拍個幾下手,在閱讀文章時如果有遇到什麼問題,或是有什麼建議,都歡迎留言告訴我,謝謝。 😃