iT邦幫忙

2021 iThome 鐵人賽

2
Modern Web

用30天更加認識 React.js 這個好朋友系列 第 32

[Bonus 系列] - 使用 useCallback & useMemo 的正確時機是什麼?

  • 分享至 

  • xImage
  •  

前言

雖然說之前已經認識了 useCallback、useMemo,但有時還是對於什麼時候要用它們的時機有些模糊,所以就透過這篇文章釐清觀念。

首先要知道一點是如果沒有必要可以不用去用它們。盲目的使用反而更耗效能,因為它們在 React 底層記憶值也是需要消耗效能的,同時程式碼也會變得更複雜。

Referential Equality

在此先複習一下 Referential Equality,對於理解後面的內容會有所幫助。

我們知道陣列、物件、函式等物件型別擁有 call by reference 的特性,所以即使有兩個物件它們裡面的元素、屬性、函式內容都一樣,在做 === 比較時仍然會是 false。

const hero1 = {
  name: 'Batman'
};

const hero2 = {
  name: 'Batman'
};

console.log(hero1 === hero1); // => true
console.log(hero1 === hero2); // => false

這樣跟 React 又有什麼關係?讓我們繼續看下去!

useCallback 使用時機

Referential Equality 案例 1

在下面的範例可以看到一個被 React.memo() 包覆的元件 Child,而且接收到一個函式 onClick 當作 props。

父元件重新渲染時,雖然都還是同一個函式,但函式被重新產生了,所以 React.memo() 會以為傳入的 props 改變而重新渲染 Child 元件,所以加上了 useCallback。

function Parent() {
  const [count, setCount] = useState(0);

  const onClick = useCallback(() => {
    console.log('click');
  }, []);

  return (
    <>
      <Foo onClick={onClick} />
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>count increment</button>
    </>
  );
}
const Child = memo({ onClick }) => {
  // ...略

  return (
    <>
     // ...
     <button onClick={onClick}>可點擊</button>
    </>
  )
});

Referential Equality 案例 2

useEffect 的 dependency array 的其中一個元素為物件型別時,若物件重新產生就會被 useEffect 當作 dependency array 改變,像以下的例子就造成了 useEffect 無限 render,所以也需要加上 useCallback。

跟此範例類似的例子在 pjchender 大大的 從 Hooks 開始,讓你的網頁 React 起來 Day20 篇 也有提到,有興趣的讀者可以跳到"在 useEffect 的 dependencies 中放入函式 - useCallback 的使用"的段落去閱讀。

const App = () => {
  const [id, setId] = useState(1);
  const [detail, setDetail] = useState("");

  const getDetail = useCallback(() => {
    fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
    .then(res => res.json())
    .then(json => {
      setDetail(json); // state updated
    }));
  }, [id]);

  useEffect(() => {
    getDetail();
  }, [getDetail]);

  return (
    <>
      <p>{detail.name}</p>
      <p>Current id: {id}</p>
      <button onClick={() => setId(id + 1)}>id increment</button>
    </>
  );
}

useMemo 使用時機

1. Referential Equality

Referential Equality 的概念也可以套用在 useMemo,所以也可以留意一下 useEffect 的 dependency array。

const App = ({ param1, param2 }) => {
  const params = useMemo(() => {
    return { param1, param2, param3: 1 };
  }, [param1, param2]);

  useEffect(() => {
    callApi(params);
  }, [params]);
}

2. 計算開銷較大的值

例如 filter 很多元素的陣列、複雜數學運算

參考資料 & 推薦閱讀

How To Use Memoization To Drastically Increase React Performance

When to useMemo and useCallback

如何錯誤地使用 React hooks useCallback 來保存相同的 function instance


上一篇
[Bonus 系列] - 來看看 React Router v6 有什麼新功能?和 v5 有哪些地方不同?
下一篇
[Bonus 系列] - 和 useEffect 很像的 useLayoutEffect
系列文
用30天更加認識 React.js 這個好朋友33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
chunwen
iT邦新手 3 級 ‧ 2022-03-16 00:24:58

蠻好奇使用 useMemo 或是 useCallback Hooks 後
整個page是否能真的提升多少效能

harry xie iT邦研究生 1 級 ‧ 2022-03-18 14:10:47 檢舉

不錯的問題!之前看 useMemo 或是 useCallback 的教學大多也是教你怎麼使用 or 何時用,到是還沒真的看過使用前後的效能比較

我要留言

立即登入留言