iT邦幫忙

2024 iThome 鐵人賽

DAY 28
0
Modern Web

剛入行就一人重新打造公司前端系統?系列 第 28

Day 28 - useCallback 到底是如何達到效能優化的目的?

  • 分享至 

  • xImage
  •  

useCallback 一直是 React hooks 中搞不太懂的地方,只知道是拿來做效能優化。直到看到《React 思維進化》這本書後,發現 useCallback 做效能優化的方式跟我原來想的完全不一樣,而且有蠻多容易搞混的點,那以下開始正文~

useCallback

useCallback 是 React 的一個 Hook,用來快取(cache)函式。

語法:

const cachedFn = useCallback(fn, dependencies)
  • fn:要快取的函式。這個函式通常依賴於 props 或 state,原因以下概念部分會再描述得更詳細。
  • dependencies :是一個陣列。(與 useEffect 看起來很像,但這邊是必填的。)
  • 運作流程:
    1. React 會在初次渲染時,將此 fn 函式與 dependencies 快取起來。
    2. 後續重新渲染時,React 會比較 dependencies
      • 如果相同的話:會忽略本次渲染時所產生的函式,然後回傳前一次渲染所快取起來的函式。
      • 如果不同的話:會快取本次渲染時所產生的函式與 dependencies,然後回傳本次渲染時所產生的函式。
      • 注意:使用 useCallback 無法避免每次渲染時重新產生函式。

為什麼 useCallback 要對函式進行快取?

既然函式會還是被創建,並不會減少「不必要的函式產生」,那是什麼原因造成 useCallback 可以拿來做效能優化呢?建議的使用情境是「如果函式會用到 props 或 state ,但又會被 useEffect 使用的情況。」,可以透過以下範例來思考。

範例

假設有一個函式 fetchData() 來根據查詢字串 query 從 API 抓取資料。如果每次重新渲染都創建一個新的 fetchData()useEffect 就會一直以為函式變了,導致不必要的 API 請求。

優化前:每次元件重新渲染都會重新創建 fetchData() ,useEffect 也都會判定依賴更新。

import { useEffect } from 'react';

function Child({ query }) {
  const fetchData = async () => {
    const response = await fetch(`https://foo.com/api/search?query=${query}`);
    // ...
  };

  useEffect(() => {
    fetchData(); // 每次渲染都會重新執行
  }, [fetchData]); // 因為 fetchData 是新的,useEffect 每次都會執行

  // ...
}

優化後:使用 useCallback 記住 fetchData

import { useCallback, useEffect } from 'react';

function Child({ query }) {
  const fetchData = useCallback(async () => {
    const response = await fetch(`https://foo.com/api/search?query=${query}`);
    // ...
  }, [query]); // 只有當 query 改變時才會回傳本次創建的 fetchData()

  useEffect(() => {
    fetchData(); // 只在 fetchData() 改變時重新執行
  }, [fetchData]); // 因為 fetchData 是記住的,不會每次渲染都執行

  // ...
}
  • query 的值與前一次渲染時的版本相同時,useCallback 就會回傳前一次渲染時所產生的 fetchData() ,然後當 fetchData() 有變化時,useEffect 才會重新執行。

所以是如何達到效能優化?

  • useCallback 這個 hook 本身的效果並不是效能優化,但卻能協助 React 中其他的效能優化手段保持正常運作,原因是「透過正確反應資料流的變化來達到效能優化的效果。」
  • 以上方範例來說,fetchData() 不會在每次元件渲染時都被重新創建而導致 useEffect 的依賴判斷失效,使用 useCallback 後能正確反應 query → fetchData → useEffect 的變化,進而達到效能優化的效果。

參考資料


上一篇
Day 27 - 儲存可變資料而不觸發重新渲染的 useRef
下一篇
Day 29 - 使用 useMemo 來做效能優化
系列文
剛入行就一人重新打造公司前端系統?31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言