SWR 隱藏版刷子:useSWRInfinite

Andy Chen
Starbugs Weekly 星巴哥技術專欄
8 min readDec 27, 2021

--

前言

之前筆者在 關於 1.0.0 版的 SWR,你該升級嗎? 這篇文章中提到很許多 SWR 這個套件的相關用法,但有一個番外篇的用法筆者只有稍微帶過而已,這個用法就是 useSWRInfinite,今天筆者就要來填補一下之前挖的坑,把 useSWRInfinite 做一個完整的介紹,讓讀者可以更容易根據不同的使用情境使用 SWR 所提供的各種方法。

使用情境

相信身為一位前端工程師或多或少都有遇過分頁(Pagination)的開發吧,當一個頁面需要呈現的資料量很大時,為了追求更良好的使用者體驗,網頁都會利用 pagination 的方式進行開發,讓網站的資料負荷量不會瞬間過高,同時也確保了使用者不會花過多的時間在等待資料。

然而 pagination 的實作其實有百百種,如果今天讀者是單純利用 table + pagination 按鈕的方式,其實用最基本的 useSWR 即可,並且利用 useSWR 的 global cache 機制,把每次的 query url 當作是 unique key 也不會造成重複打 api 的狀況,也就是下方圖片的樣式。

但假如今天讀者的呈現方式是用瀑布流的方式呈現,也就是將所有的資料都集中在同一個區塊,只是會不斷的 Load More 來獲取其他分頁的資料,這種時候就可以利用 useSWRInfinite 讓整體寫法更加簡潔。

useSWRInfinite

useSWRInfinite 讓我們可以利用一個 hook 進行多個 request 資料保存動作,與一般的 useSWR 不同,useSWR 抓取的是該 key 的資料,而 useSWRInfinite 則是抓取到該 key 之前所有資料的集合。

useSWRInfinite 所回傳的內容基本上與 useSWR 一致,只是在 response data 中新增了兩個不同的 key,接下來就稍微說明一下這兩個 key 的差別吧!

  • size

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

  • setSize

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

動手實作

其實我們也可以自己手動實作 useSWRInfinite 的儲存資料過程,只要利用 React 的 useRef 以及 useMemo 即可達到該效果,寫法可以直接看下圖。

useRef 會建立一個 mutable 的 object,藉由 useRef 的效果,我們可以確保使用此 object 時,即便元件 re-render 了還是可以拿到同一個 object,並且當我們更新此 object 的 current 屬性時也不會造成 re-render 的狀況,也就可以紀錄先前的帶入進來的值了。

最後是筆者的習慣,為了確保每次更新過後的資料都可以被記憶起來,所以筆者會習慣用一個 useMemo 把 merge 過後的新資料包裝起來,由於我們也在 useMemo 中增加了一些判斷式,所以就不會那麼容易可以更新,在畫面上也就不會因為這筆資料進行多次的 re-render。

講完此 custom hook 的實作後,接下來就可以看一下要如何跟 useSWR 進行搭配,按照慣例我們一樣先看寫法再來解析。

可以發現只要將 shouldMerge 的 function 寫好即可讓每次抓取的新資料都可以順利與原始資料作合併,這時候就會有 useSWRInfinite 的效果了,而且我們也有在 custom hook 中做了一些判斷,讓 ref object 不會一直被更新,這樣拿到的資料也不會一直都不同造成 re-render 了,也算是一種提升效能的方式。

react-infinite-scroll-hook

react-infinite-scroll-hook 是一個相當好用的 scroll hook,可以用來偵測使用者是否滾動到我們要觸發行為的區塊,利用此 hook 並且結合 useSWRInfinite 就可以達到滾動更新資料的瀑布流效果。

react-infinite-scroll-hook 需要帶入的參數是一個 object,這個 object 會可以帶入以下幾種 key:

  • loading

boolean type,用來判斷是否正在抓取資料,此 key 為必填。

  • hasNextPage

boolean type,用來判斷是否需要進行 Load more,此 key 為必填。

  • onLoadMore

function type,用來進行 Load more 的動作,此 key 為必填。

  • rootMargin:

string type,用來設定什麼時候會觸發 onLoadMore ,要能順利讓 rootMargin 有作用的前提是 Loading 元件必須要一直保留在頁面上不能隨意 unmount ,如此一來 rootMaring 才會有作用,而 rootMargin 的順序跟寫 css 時一模一樣,此 key 為選填。

  • disabled

boolean type,用來設定是否需要停止繼續滾動更新,通常會用於抓取資料失敗後,此 key 為選填。

  • delayInMs

number type,用來設定多少毫秒後再觸發 onLoadMore 的 function,預設為 100 毫秒,此 key 為選填。

帶入了這幾個參數後,react-infinite-scroll-hook 會回傳一個陣列,這個陣列第一個元素是一個 ref object,這個 ref object 可以幫助 react-infinite-scroll-hook 知道當使用者滾動到哪個元素上時,就要進行 Load more 的動作,如此一來就可以達到滾動更新的效果了。

其實 react-infinite-scroll-hook 回傳的陣列還有第二個元素,第二個元素是一個 object 裡面有一個 key 叫 rootRef ,這個 rootRef 是給 parent node 用的,假如讀者今天也需要透過滾動的方式監控父元素,就可以利用此方式,寫法也很簡單如下圖:

與 SWR 的結合

最後來講一下 react-infinite-scroll-hook 與 useSWRInfinite 的結合,透過這兩個好用的利器就可以很輕鬆的完成瀑布流效果,寫法的部分如下圖:

相信上面的結合應該不會太難理解,我們可以利用 react-infinite-scroll-hook 來觸發 useSWRInfinite 的 revalidate,react-infinite-scroll-hook 會不斷的更新 useSWRInfinite 所回傳的變數,藉此達到更新下一筆資料的效果,而開發者只要把 react-infinite-scroll-hook 所回傳的 ref object 擺放到正確的元素上即可。

Demo time

最後的 Demo time 請容許筆者偷懶一下用上次的文章作為 Demo,畢竟前面的鋪成就是為了達到滾動更新的效果,而這個效果在上次的文章已經做好了,這邊筆者就偷偷搬移過來做為示範吧XD

同時附上 codesandbox 的 Demo 程式碼讓有興趣的讀者可以上來玩玩看XD

小結

這次文章終於把 SWR 這個好用的 data fetching library 介紹完了,假如讀者未來在開發網頁而且網頁的架構不會到太複雜時,不妨也可以利用 SWR 提升整個網站的效能,同時也因為有了 global cache 的機制,讓網站不需要太頻繁重複打 api 拉資料。

假如今天的網站非常仰賴 api 回傳的資料做顯示,想使用 redux 又覺得 redux-sagaredux-observable 等工具太難學習,不妨可以直接使用 SWR 一個套件處理所有的事情,非常乾淨俐落推薦讀者可以使用看看,一起進入 SWR 的世界吧XD

Reference

--

--

Andy Chen
Starbugs Weekly 星巴哥技術專欄

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