iT邦幫忙

2024 iThome 鐵人賽

DAY 23
0
Modern Web

前進React 生態系 : 技術應用與概念解析系列 第 23

Day 23 - 掌握 React Memoization : React.Memo 、useMemo 和 useCallback

  • 分享至 

  • xImage
  •  

在使用 memoization 之前

為什麼需要使用 Memoization ?

避免非必要的重複渲染(re-render) 是性能優化的一個重點。在大部分情況下,re-render 並不會對性能造成明顯的影響,除非是渲染頻繁或者使用複雜的運算。

使用前檢查事項

在引入 memoization 前,應先檢查以下幾點,避免不必要的重複渲染,這樣可以減少不必要的優化:

  • 是否將 useState , useEffect 或 custom Hook 放在正確合適的位置
  • 是否有非必要的 useEffect
  • 是否在 useEffect 中加上不必要的 dependencies,像是如果函式只有在 useEffect 使用,可以移到 useEffect
  • 是否將 Component 拆分的足夠小且明確,另外最好不要在一個 Component 中又 render 一個 Component
  • 和 state 無關的函式是否在元件外面宣告
  • 是否是非常複雜的運算

什麼算是「非常複雜的計算」?

可以用以下工具來評估運算是否足夠複雜到需要優化:

  • React DevTools Profiler:用來觀察元件的渲染時間和次數。
  • console.timeconsole.timeEnd:可以用來計算特定運算的執行時間。

範例如下:

console.time("filter array");
const visibleTodos = filterTodos(todos, tab);
console.timeEnd("filter array");

優化 re-render 的小技巧

如果狀態相關的內容,在需要複雜運算的元件上方的某個位置使用時,很容易會使用 React.memo 來處理,但實際上可以將狀態相關的內容封裝成一個元件。

使用前:

export default function App() {
  const [color, setColor] = useState("red");
  return (
    <div style={{ color }}>
      <input value={color} onChange={(e) => setColor(e.target.value)} />
      <p>Hello, world!</p>
      <ExpensiveTree />
    </div>
  );
}

使用後:

export default function App() {
  return (
    <ColorPicker>
      <p>Hello, world!</p>
      <ExpensiveTree />
    </ColorPicker>
  );
}

function ColorPicker({ children }) {
  const [color, setColor] = useState("red");
  return (
    <div style={{ color }}>
      <input value={color} onChange={(e) => setColor(e.target.value)} />
      {children}
    </div>
  );
}

ColorPicker 元件內的狀態不會影響到 ExpensiveTree 的渲染,減少不必要的 re-render。

React 的 memoization 是怎麼做運作的?

React 的 memoization 機制針對每個元件或計算只儲存一個快取結果。

React.memo 為例,當元件使用 React.memo 來包裝時,會記錄最近的 props 和渲染結果。當元件接收到新 props 時,React 會檢查這些 props 是否與之前的 props 相同。如果 props 相同,React 會返回之前的渲染結果(即快取的結果)。如果 props 不同,則會重新計算並渲染新的結果。

React.memo 是比較 props ,而 useMemouseCallback 則是比較 dependencies 的內容。

React.memo

React.memo 是一個 higher-order component,用來包住元件以避免不必要的重新渲染。透過比較新舊的 props 是否相同來決定是否重新渲染,使用 Object.is 比較,React.memo 也有第二個可選參數用來自訂比較的方法,會是一個接受兩個參數的函數:元件之前的 props 和新的 props。

使用時機:經常使用相同的 props 重新渲染時。

const MemoizedComponent = memo(SomeComponent, arePropsEqual?)

useCallback 和 useMemo

useCallback是用來記住某個函數的引用,避免函數在每次渲染時重新創建。而 useMemo 則是用來記住一段昂貴計算的結果。透過 dependencies 用來決定是否重新渲染,並使用 Object.is 來比較兩個值是否相等。

使用時機:

  • 當 props 被用作另一個 hook 的 dependency 。
  • 當 Component 被包在 React.memo 中,因為每次需渲染時物件和函數會重新創建。
  • 當涉及複雜運算時且 dependencies 很少改變。

上一篇
Day 22 - 掌握 React 19 Metadata 與資源載入優化
下一篇
Day 24 - React Compiler 簡介
系列文
前進React 生態系 : 技術應用與概念解析30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言