iT邦幫忙

2023 iThome 鐵人賽

DAY 21
0
Modern Web

react 學習記錄系列 第 21

[Day21]我的 react 學習記錄 - useCallback

  • 分享至 

  • xImage
  •  

這篇文章的主要內容

簡單介紹優化 react 的另一個方法 - useCallback


useCallback

在 react 裡面除了 object 跟 array 以外 function 也是一個非常被拿來當做 props 傳遞的東西,所以也會有重複 create 導致 memo 不起作用的狀況,當然這個情況也可以使用上一篇介紹的 useMemo 來處理。

const handleSubmit = useMemo(() => {
  return (orderDetails) => {
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails
    });
  };
}, [productId, referrer]);

在 function 裡面又再 return 一個 function,有點難以閱讀跟馬上理解,這個時候可以使用 useCallback 來處理。


Syntax

const cachedFn = useCallback(fn, dependencies)

fn: 一個希望 react 幫我們記住的 function,可以接收任何參數,也可以回傳任何 value,react 會回傳你所放入的這個 function,但不會執行,讓你在想要使用的地方呼叫這個 function。

dependencies: 一個 array 裡面應該放著在 fn 裡面有使用到的外部變數,跟其他的 dependencies 一樣 react 會在 re-render 的時候透過 Object.is() 一個個進行比較,當結果有 false 的時候便會給你新建立的 function。

cachedFn: 初次 render 的時候會跟你放入的 fn 相同,在 re-render 的時候 react 會透過檢查 dependencies 來確認是否有需要給你新的 function。

注意事項

  • useCallback hook 只能在元件裡使用,且只能在元件的最外層使用,不能放在迴圈或是判斷式裡,如果有需要可以建立一個新的子元件,放在子元件裡。

簡單用個範例來觀察一下 useCallback 是怎麼運作的。

import { useState, memo } from "react";

type Props = {
  name: string;
  value: number;
  onClick: () => void;
};

const Item = memo(function Item({ name, value, onClick }: Props) {
  console.log(`Item ${name} render`);
  return (
    <div>
      <p>
        {name}: {value}
      </p>
      <button onClick={onClick}>add {name}</button>
    </div>
  );
});

function App() {
  const [count, setCount] = useState<number>(0);
  const [number, setNumber] = useState<number>(0);

  const handleCount = () => {
    console.log(`handleCount --> number:${number}, count: ${count}`);
    setCount(count + 1); // 每次點擊 count +1
  };

  const handleNumber = () => {
    console.log(`handleNumber --> number: ${number}, count: ${count}`);
    setNumber(number + 1);
  };

  return (
    <div>
      <Item name="Count" value={count} onClick={handleCount} />
      <Item name="Number" value={number} onClick={handleNumber} />
    </div>
  );
}

用 count 跟 number 兩個 state 來觀察 re-render 時候 function 被建立時的狀態。

useCallback-1

可以看出不管觸發哪一個 function 出現的 value 都會是 re-render 之前最新的狀態,代表每一次 state 改變都有重新宣告新的 function。

而且我們的 memo 沒有起作用每一次 state 改變 Item 還是進行了 re-render 了。

接著就來使用 useCallback 吧。

import { useCallback, useState, memo } from "react";

type Props = {
  name: string;
  value: number;
  onClick: () => void;
};

const Item = memo(function Item({ name, value, onClick }: Props) {
  console.log(`Item ${name} render`);
  return (
    <div>
      <p>
        {name}: {value}
      </p>
      <button onClick={onClick}>add {name}</button>
    </div>
  );
});

function App() {
  const [count, setCount] = useState<number>(0);
  const [number, setNumber] = useState<number>(0);

  const handleCount = useCallback(() => {
    console.log(`handleCount --> number:${number}, count: ${count}`);
    setCount(count + 1); // 每次點擊 count +1
  }, [count]);

  const handleNumber = useCallback(() => {
    console.log(`handleNumber --> number: ${number}, count: ${count}`);
    setNumber(number + 1);
  }, [number]);

  return (
    <div>
      <Item name="Count" value={count} onClick={handleCount} />
      <Item name="Number" value={number} onClick={handleNumber} />
    </div>
  );
}

這邊兩個 useCallback 應該都有亮起來,會請我們把使用到的變數放到 dependencies 裡,因為在 console.log() 裡面兩個都有使用到。

useCallback-2

可以看到第一次執行 handleNumber 的時候 console 出來的 count 是 0,代表我們執行的是最初畫面初始化時候的 handleNumber,而且我們的 memo 也起作用了當 props 沒有改變的元件不會出現多餘的 re-render。

通常會在兩個情況下使用 useCallback

  • 當希望減少子元件 re-render 的頻率時,傳到子元件的 props 是 function,所以 memo 不會起作用。
  • 也許 useCallback 回傳的 function 可能是其他 react hook 的 dependencies 所以為了減少其他 react hook 的執行次數。

useCallback 並不會因為 dependencies 一樣就不產生新的 function,react 還是會產生新的 function 但是 react 會依照 dependencies 的比較結果,決定要給你 cache 的 function 還是新產生的 function。

另外 useMemo 跟 useCallback 因為語法上很接近,所以容易搞混。

兩者的差別就是 useMemo 會執行傳入的 function 並且可以回傳任何的 value,useCallback 不會執行傳入 function,而是回傳被傳入的function。


useCallback - react document

下一篇簡單介紹 lazy 跟 Suspense 的用途跟用法
如果內容有誤再麻煩大家指教,我會盡快修改。

這個系列的文章會同步更新在我個人的 Medium,歡迎大家來看看 👋👋👋
Medium


上一篇
[Day20]我的 react 學習記錄 - useMemo
下一篇
[Day22]我的 react 學習記錄 - lazy & Suspense
系列文
react 學習記錄30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言