關於 1.0.0 版的 SWR,你該升級嗎?

Andy Chen
Starbugs Weekly 星巴哥技術專欄
13 min readOct 27, 2021
Photo by Clément Hélardot on Unsplash

前言

不曉得讀者有沒有使用 SWR 這個好用的獲取資料的函式庫?筆者我是從 SWR 還在 0.3 時期就在用了,可以說是相當前期就在使用這個好用的函式庫,而目前的 SWR 已經正式進入 1.0 的版本了,更新速度可以說是相當快。

如果對於 SWR 想要更深入理解的讀者不妨可以先參考我們團隊中神Q超人 所撰寫的 開源專案讀起來 | 聽說可以幫你保管資料再決定要不要更新的 SWR ,而筆者也會在這篇文章中稍微介紹一下 SWR 的用法,並且帶入以前的版本與現在的版本差異,讓使用者會有更深刻的印象!

什麼是 SWR

SWR 是由 vercel 團隊所開發的一套基於 React Hooks 架構下的 data fetching library,vercel 除了開發 SWR 外還有另一套在 React 中很常被用來作為 SSR 框架的 Next.js,所以這個團隊可以說是相當強大,其開發的工具都非常值得學習使用。

為什麼我們需要 SWR

講了那麼多還是沒有提到 SWR 最厲害的地方,通常我們在做 data fetching 的時候最怕的就是會拉到重複的資料,可能是因為本身程式架構上的問題產生 re-render 的問題讓同一支 api 被重複打,亦有可能是因為忘記把這些資料利用 state 的方式保存起來,導致每次進行相關的 actions 都必須要重新拉資料。

為了避免這個情況早期會在 componentDidMount 的生命週期或者是寫一個 useEffect 的方式進行 data fetching,如此一來就可以解決 api 重複打的問題,但這樣對於整體的效能並不是特別好,想想看我們每次網頁都必須要讓 DOM tree 完全遍歷後,接著是拉資料,再來做 re-render 並且更新資料上去,這些步驟相當繁瑣而且會讓整體的等待時間變長,為了不要讓這件事情發生,在前端的世界中誕生了一種叫:Optimistic UI(樂觀 UI) 的開發模式。

在 Optimistic UI 的世界中,理想情況是每一次的 data fetching 都可以獲得資料,也因此在開發上其實可以先在會使用到該資料的區塊,先把需要使用到的資料欄位寫上去,即便是空值也無所謂這樣在更新 DOM tree 時也不用整棵樹重新更新,只需要更新部分節點即可,如此一來也能提升使用者體驗。

而 SWR 就可以做到上述兩點,既可以避免重複打 api 甚至還可以幫你 cache 住 fetch 回來的資料,同時也可以透過 SWR 提供的 options 來達到 Optimistic UI 的效果,可以說是學習一套工具達到多種效果相當便利。

0.X 時期的 SWR

其實 0.X 時期的 SWR 就提供了許多很好用的 method,筆者最喜歡 SWRConfig 這個 Provider ,這個 Provider 可以讓你先把一定會用到的 Options 先傳入,讓後續的 useSWR hooks 都可以先使用這些預設的 Options,想要知道有哪些 Options 可以使用的讀者可以參考這個網站,這邊筆者只會增對幾個常用的 Options 做解說。

首先讀者可以發現上面的範例中,在 index.js 這個檔案筆者寫了三個 Options 到 SWRConfig 的 value 物件中,這三個分別是 revalidateOnMountrevalidateOnFocus 以及 fetcher ,接下來就稍微細說這三個 Options 代表的含義。

  • revalidateOnMount

revalidateOnMount 顧名思義就是當 component 的生命週期是 mounted 的時候就要進行 data fetching,這個筆者蠻建議設定為 true 的,這樣也可以確保 component 一定會在 mounted 的時候是有最新的資料。

  • revalidateOnFocus

revalidateOnFocus 顧名思義就是當元素被 focus 時就要進行 data fetching 以確保該元素的資料永遠都會是最新的,但這樣做其實有點擾人,而且可能會對 server 造成不必要的資源浪費,但 SWR 預設都會設為 true ,因此筆者都會自己把這個設定關閉,如此一來就不會過於頻繁的進行 data fetching 了。

  • fetcher

fetcher 就不用多說明了,就是將共用的 fetcher function 拿出來使用,相信在開發的過程中不管是用原生的 fetch 亦或是使用 axios 進行 data fetching,應該都會把這些 data fetching 的過程寫成一個共用的 utility,之後的 api 就只要傳入相關的 URL 或 payload 進去即可,如果讀者有這種習慣的話,這邊就可以直接帶入共用的 fetcher 即可,相關範例碼可以參考上面範例的 service/fetcher.js 這隻檔案。

講完 SWRConfig 常用的幾個 Options 後接下來就來介紹一下 useSWR 可以怎麼搭配使用。

首先我們看官方範例,發現 useSWR 一共要帶入這三個參數: keyfetcher 以及 options ,不過聰明的讀者應該有發現這邊的 fetcher 竟然是 optional,就是因為在 SWRConfig 中我們可以帶入 fetcher 的關係,導致這邊的 fetcher 其實是一個 optional parameter。

由於我們把 fetcher 傳入 SWRConfig 了,這邊 useSWR 的參數就只要傳入 data fetching 的 URL 就好,而且 SWR 還會很聰明地把每次的 data fetching URL 當成 key 並且將其回傳的 data 做一個 cache,這樣日後只要有用到這個 data 就不需要重新打 api 了效果就會像下面這樣:

再來就是 Options 了,我們在 SWRConfig 中只會傳入共用的 Options,而為了達到 Optimistic UI 的效果,我們可以在每次的 useSWR 中傳入 initialData ,這樣一開始 useSWR 回傳的 data 就會是 initialData 中的物件,就可以讓我們先做初步的 Optimistic UI 的設計可以說是相當方便。

所以讀者看上面的範例碼時就會看到筆者即便還沒有進行 data fetching 也可以直接把 data 做 map 的處理並且回傳相關的 element 。

講完了 parameter 後接下來就來講講 useSWR 的 response data,在 0.X 版本的 SWR 的 response 會是一個物件,裡面包含了以下這幾個 key dataisValidatingerrorrevalidatemutate ,接下來筆者會一一介紹這個 key 的作用:

  • data

data 顧名思義就是回傳的資料,但這邊有個小提醒是 SWR 會把回傳的資料外面再用一層 data 物件包裝起來,所以結構會像這樣:

所以讀者不妨在使用 data 前可以先進行解構再拿出來使用會更加方便喔!

  • isValidating

isValidating 是一個 boolean 值,會在進行 data fetching 的時候進行值的改變,所以當目前正在 fetching 的狀態時,isValidating 就會是 true ,所以可以利用 isValidating 的值來判斷目前是不是正在進行 data fetching。

  • error

相信 error 就不用多說明了,就是 data fetching 失敗時會回傳的 error。

  • revalidate

revalidate 是一個 function 用來重新獲取資料,只要呼叫 revalidate 就會重新搭資料,效果就像下面這樣:

  • mutate

mutate 是一個 function 用來 cache 確定不會改變的資料,通常會用於資料更新的時候可以順便 cache 住使用者輸入的資料,並且更新 useSWR 中 response 物件中的 data,而這邊 mutate function 要帶入的參數是 data object,讀者要特別注意喔!

最後將所有的觀念合一起就會長得像下面這張圖這樣:

眼尖的讀者可能會發現這裡筆者在 apiURL 中做了一個判斷,讓 apiURL 有可能是 null,這是因為當 SWR 發現 key 是 null 時就不會進行任何的 data fetching 就可以降低 server 的負擔。

1.0 時期的 SWR

基本上 1.X 版本與 0.X 版本的寫法差異不大,只在於有些 Options 消失或者重新命名了,但其實筆者一開始很不習慣,因為 0.X 版本的 useSWR 在 response 物件中有提供一個好用的 revalidate function,透過 revalidate function 我們可以很簡單的讓資料重新獲取,可是這項功能在 1.X 版本中被拔掉了,統一由 mutate 進行資料管理,所以假如讀者像筆者一樣有使用 revalidate function 這種習慣的話在升級的時候就要特別小心喔!

至於 mutate 的部分則是新增一個參數叫 shouldRevalidate 這個參數是一個 boolean 值,預設為 false 所以今天讀者想要進行 revalidate 的動作很簡單,只要多帶一個為 true 參數即可。

而 SWR 的寫法其實不只剛剛講的 SWRConfig 搭配 useSWR 一種而已,也可以直接單純寫 useSWR 就好,因此這邊的範例筆者就會介紹另一種寫法給讀者參考,那我們就開始另一種寫法的介紹吧!

由於我們沒有先在 SWRConfig 中傳入 fetcher,所以這邊的 useSWR 就必須要帶入 fetcher,而這邊的 fetcher 其實就可以帶入每一個已經先跟共用的 fetcher 組合好的 api service,範例的部分可以參考 /service/album.js 這隻檔案。

這邊筆者稍微介紹一下 useSWRkey 這個參數的強大寫法, key 這個參數除了可以傳入字串外其實還可以傳入陣列,而 SWR 也會很聰明的將陣列中的元素進行解構並且把這些元素當成是 fetcher 的參數傳給 fetcher,一樣如果不要讓 SWR 進行 data fetching 也可以讓這個 key 是 null 就好。

如果要確保每一次的 key 都不會重複導致 SWR 拿錯資料回來,筆者建議可以在新增一個字串是這個 apiURL 或者 apiServiceName 並且把這個字串擺在陣列的最後一個元素,這樣就可以確保每次即使陣列中有元素變動也會是在這個 api scope 中變動,不會影響到其他的 api,所以寫法就會像下面這樣:

再來要講一個 1.X 版新增的 Options 叫 fallbackData ,這個 fallbackData 其實跟 0.X 版本的 initialData 差不多,但差別在於假如今天 data fetching 失敗了,SWR 會回傳 fallbackData 的物件回來,而不是像 0.X 版本的 SWR 直接回傳一個空值回來,所以筆者建議這個 fallbackData 一定要設定真的太重要了,而官方也有針對這個 fallbackData 進行解說,可以參考下圖:

最後 1.X 版本跟 0.X 版本差不了太多,只有 response 物件中少了 revalidate 這個 function,全部結合在一起就會像下面這樣:

番外篇

這邊筆者來個番外篇一下,網路上的文章對於 SWR 的介紹其實蠻多的,但筆者發現蠻少文章提到 SWR 另一個很好用的 hooks 叫 useSWRInfinite ,當我們對於 data fetching 要進行瀑布流效果時,這個 useSWRInfinite 可以說是相當好用,這裡筆者還會推薦另一個好用的 scroll hooks 叫 react-infinite-scroll-hook,將這兩個 hook 結合就可以很輕鬆的達到滾動更新的效果,可以說是相當好用的利器。

useSWRInfinite 的參數基本上跟 useSWR 一樣這邊就不多做贅述,唯一的差別在於 response data 中額外新增了兩個 key 一個是 size 另一個是 setSize 接下來就細說這兩個的差異:

  • size

size 簡單來說就是要目前要抓取第幾頁的 data,可以想像成是 indexoffset 的概念應該會比較好理解,透過 size 我們就可以很方便的控管要抓取的資料範圍。

  • setSize

setSize 就是設定目前要抓取的 size,簡單來說就跟 useState 的 setter 一模一樣,相當好理解。

最後效果就會像下面這樣:

小結

其實認真硬要說的話,0.X 版本與 1.0 版本相差可以說是相當少,只有幾個 Options 被重新命名以及新增了幾個 Options 可以使用,但如果只是寫法上的差異說真的不用刻意升級也沒差,可是 SWR 在 1.0 版本中做了許多效能上的優化,像是更小的 bundle size,據官方所說在核心程式碼的部分少了 41% 的大小,而 package 的部分也少了 52% 的大小,也改進了 tree-shaking 讓程式碼可以更有效率的 bundle 起來。

講了這麼多其實 SWR 在他們發布 1.0 版本時有寫了一篇文章,是關於 1.0 版本做了那些改變,可以說是相當用心,有興趣的讀者不妨可以點擊這個網站來閱讀。

所以總結來說 1.0 版本是相當值得升級的,除了好用的 fallbackData 以外還有 bundle size 變小、improved tree-shaking 等等功能,這些對於前端來說可以說是相當重要的功能,所以如果讀者本身是使用 0.X 版本的 SWR 看完這篇文章後可以花時間來更新版本了XD

參考資料

--

--

Andy Chen
Starbugs Weekly 星巴哥技術專欄

嗨嗨我是Andy,用嘴巴工作的工程師😂,喜歡學習不同領域的內容,專長為網頁開發,歡迎大家跟我聊技術~