iT邦幫忙

2022 iThome 鐵人賽

DAY 5
1

https://ithelp.ithome.com.tw/upload/images/20220918/20151334psTaRwUInp.png

網頁中難免會有排序/過濾/資料整理等等的操作,有些列表式的查詢更可以一次列出一千多筆,只想排一下順序網頁就開始跟你鬧「彆扭」,這次來看看 useMemo 可以如何幫我們解決吧!

還記得當 state 改變時,component 也會跟著 re-render,其所有的內容都會跟著被重新建立,若裡面包含了大量運算,每次 render 就勢必要跟著再運算一次 (雖然結果一樣),因此 useMemo 可以幫我們做 Memoization ,減少不必要的體力(?)。

本篇DEMO

Usage

const memoValue = useMemo(() => someExpensiveCalculation(arg), [])

useMemo 會接受一個回傳的值,第一次執行後,只有在[ ] (dependencies) 內的內容被改變時,才會重新執行,除此之外, useMemo 回傳的值永遠會是相同的

Example

假設我們有一組電影清單,想要按照年份(year)或是電影名稱(title)來切換排序

你可能會想這樣做:

  • 排序(year/title)用 useState 管理
  • 另外用變數 sortedData 依照狀態來排序資料
const movies = [...]

function MovieList () {
  const [sort, setSort] = useState('year')
  
  const sortedData = [...movies].sort((a,b) => {
    if(sort === 'year') {
      return a.year - b.year
    }
    
    return a.title > b.title ? 1 : -1
  })
  
  return ...
}

這樣的嘗試是可以的,但延伸了一個問題:

每當 sort 改變 (或其他的state),元件會進行re-run,而 sortedData 就會重新執行一次 (指sort)

因此我們可以使用 useMemo 將兩種排序方式先計算一次,並在後續的 re-render 中仍然使用同樣的 value (此處可以透過DEMO來查看 console.log 的輸出)

const dataSortByYear = React.useMemo(() => {
  console.log("sort by year");
  return [...data].sort((a, b) => a.year - b.year);
}, []);

const dataSortByTitle = React.useMemo(() => {
  console.log("sort by title");
  return [...data].sort((a, b) => (a.title > b.title ? 1 : -1));
}, []);

const sortedData = sort === "year" ? dataSortByYear : dataSortByTitle;

useMemo & useCallback 的差異

「那這樣是不是把所有東西都套上 useMemo 就好惹?」

No! 與前一篇的 useCallback 相同,這樣的動作並不是免費的(換言之都有成本),因此你可以在幾個情況下使用:

  • Referential equality:傳遞時確保能夠永遠都是同一個 object
  • Expensive calculations:節省不必要的昂貴運算

而概念上:

  • 使用 useMemo 來記憶開銷很大的才能得到 value。 (但第一次還是得運算一次)
  • 使用 useCallback 來記憶 function 於不同元件之間的傳遞或是 render 仍能維持一樣。

前一篇有提到,當想使用 useCallback 時,可以思考看看是不會換個位置放就能解決問題 (換個位置:放到 React Component 的外面/useEffect裡面),而 useMemo 使用頻率相比 useCallback 就會高一些。

分享當初寫這兩篇時參考的其中文章: Kent C. Dodds - When to useMemo and useCallback


上一篇
[DAY 04] 記憶吐司的 useCallback
下一篇
[DAY 06] useRef 與一般的變數有甚麼不同?
系列文
React Hook 不求人,建立自己的 Hook Libary30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言