React SWR | 取得遠端資料的殺手級函式庫

你的資料 cache 了嗎 ?

Leo Chiu
手寫筆記
9 min readNov 29, 2019

--

前言

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 如何使用,以及它強大的效果。

GitHub Star History

Basic Data Loading

👉 按這裡看程式碼

SWR 的起手式非常簡單,在以上的範例中 useSWR 帶有 2 個參數:

  • 第 1 個參數為 key: key 是一個字串,作為資料的來源。
  • 第 2 個參數為 fetcher: fetcher 被用來傳入如何取得資料的函式,例如: 請求 API 經常使用的 fetch 或是 axios,將它們包裝成函式傳入。

範例中 SWR 的回傳值有 dataerror,它們分別為 fetcher 的回傳結果,以及從 fetcher 被丟出的錯誤。

在資料尚未載入時,data 都是回傳 undefined,因此,我們就可以利用 data 的回傳值判斷要不要渲染資料,用這種方式實現非同步渲染的效果。

Fast Navigation

如果一開始 API 已經成功回傳資料,並成功建立 cache,在元件切換時,SWR 能夠快速地從 cache 中讀取需要的資料。

而且請求 API 程式碼與前一個「Basic Data Loading」的範例一模一樣,不用修改任何的程式碼就可以達成。

👉 按這裡看程式碼

Dependent Fetching

👉 按這裡看程式碼

我們在使用 REST API 時很常會遇到這樣的情況,有一個 API 必須等待另一個 API 的回傳值才能發出請求。以往我們在這種情況下也許會使用 thenPromiseawait/async,或是使用套件 react-observable、react-saga 等,以上幾種方法撰寫額外的程式碼,才能讓非同步的請求可以相互等待。

SWR 能夠幫助我們輕易的解決 API 依賴的問題,像是我們想要請求一位 user 的 posts,只要把請求 user 的回傳值放到請求 posts 的 key 中,SWR 就會在拿到 user 的資料後才發出請求,讀取這位使用者的 posts。

Conditional Fetching

👉 按這裡看程式碼

有時候,我們希望 API 的請求是能夠由使用者自己掌握的,而不是在元件載入時就自動發出請求。像是常見我們在購物網站看到「查看更多」的按鈕,在點選按鈕後才載入更多的商品資訊。

要使用 SWR 達成這件事也很簡單,上面的範例,在元件載入時 fetchSwitchfalse,讓 useSWRkeynull,因此不會第一時間就請求 API。在使用者點擊按鈕後fetchSwitch 變成 trueuseSWR 才成功獲得 key 並請求 API 。

Multiple Arguments

👉 按這裡看程式碼

我們平時使用 API 時,不一定只會用到一個參數,像是 GET 時,我們也許會帶一些額外的訊息,告知我們要的是 id 為 20 的 post;一些有權限限制的 API,也會要求 Header 必須帶有 Token。

❌ 然而,要注意的是在使用 SWR 時,不要這樣做:

原因是,SWR 決定要不要 refetch 取決於第一個參數 key 有沒有改變,如果像是上述這樣使用,儘管 postId 變動了,key 仍然是 /api/data,因此,useSWR 的回傳值就會是錯誤的。

👌 正常的使用方式是將 key 變成 array,把 postId 放 array 中:

如此一來, 當 postId 改變時,SWR 就可以成功被通知並發送 API 的請求。

你以為這樣就沒問題了嗎 ?

因為 SWR 用的是 shallow compare,當參數的型態是 Object 時,比較的是 reference。因此,在每次元件渲染時,reference 都會被重新分配,SWR 會誤以為 key 改變了,會再次發送一次請求。

將 Object 直接帶入 key 中,會讓 React 直接炸開,跑出以下的錯誤訊息:

Using useMemo to solve this problem

👉 按這裡看程式碼

在使用多個參數時,如果參數的型態是 Object,我們必須使用 React Hooks 中經常使用的 useMemoid 記錄起來。下次在重新渲染時,useMemo 發現如果 id 的數值沒有變動,將不會重新分配新的記憶體給 params,就不會引發 React 無窮渲染的問題。

Refetch On Interval

👉 按這裡看程式碼

在有些情況下,我們希望在頁面上的資訊可以定時自動更新,使用者不用重新整理網頁就可以看到新的資料。

SWR 提供了 useSWR 的第 3 個參數讓我們可以設定請求 API 的頻率,以上範例中每 0.5 秒就會請求 API 一次,在完成後也會同時觸發元件的重新渲染。

第 3 個參數是一個物件,有許多額外的選項可以設定,詳情請洽 github

取自官方範例 https://swr.now.sh/#refetch-on-interval

這種方法也可以用在多個視窗上,如此一來,在 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」,現在正在等待修復中。

Reference

--

--

Leo Chiu
手寫筆記

每天進步一點點,在終點遇見更好的自己。 Instragram 小帳:@leo.web.dev