跳至主要内容

[Behind React] Components updates and useCallback

組件的更新與 Props 關係、以及 useCallback 的功用

在 Components updates and React.memo() 的文章中,主要是針對組件的重新渲染去做優化。這篇會進一步紀錄的是關於 "function(函式)" 的優化。
延續 Count 的例子,我們知道 Button 接受的是一個由父組件 App 所定義的 handleClick 函式。這個函式基本上每次的任務,也就是內部的邏輯都一樣,就是把當前的 count + 1,那為什麼函式看似沒變的情況下,都會導致點擊 Button 後,重新 render Button?

child component update

  • -- 原因是 JavaScript 的機制,使得 handleClick 每次在 App re-execution 時都會重新被創造,所以 "handleClick" props 被認為是改變了。因為函式在 JavaScript 中屬於 non-primitive type values,即便 function 內的邏輯分毫不差,他們仍是不同的東西。
  • -- useCallback 則可以優化這種情況:同樣花費一些效能,先比較定義 useCallback 時所給的 dependencies,若 dependencies 有改變才重新生成 function
  • -- 因此,當使用 useCallback() 時,React 會做兩件事:
  • -- 1. 儲存先前的 function
  • -- 2. 進行 dependencies 比對 -> 不同時才 re-create function
  // handleClick 經過 useCallback 的包裝後,由於我們的 dependency 是給空陣列,所以永遠都不會 re-create
// 但始終記得有 dependency 就要給,這邊是剛好沒有依賴項
const handleClick = useCallback(() => {
setCount((prev) => prev + 1);
}, []);

如果直接用 useState 的 setState 作為 Props 呢?

上面我們是把 setState 包在 handleClick 裡,但若將 setState 直接往下傳,那麼行為將是不一樣的!

child component update

  • -- 在這個例子中,我們沒有使用 useCallback 包裝 setState,但是點擊 Button 卻不會造成 Button 的 re-render
  • -- 這是由於 React 本身內部對 useState 機制:React guarantees that setState function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the useEffect or useCallback dependency list.
    這句話在新版的 React doc 沒特別看到,所以暫時先參考舊版的 doc https://legacy.reactjs.org/docs/hooks-reference.html