iT邦幫忙

2022 iThome 鐵人賽

DAY 19
1
Modern Web

你 React 了嗎? 30 天解鎖 React 技能系列 第 19

[DAY 19] useCallback 函式記憶體

  • 分享至 

  • xImage
  •  

[情境劇場]

解師傅:嘿!想跟你調整一下菜盤數量,今天會請六月天樂團來表演
小當家:這個昨天不是講過了!?更新後的菜色我已經在準備了
解師傅:恩~對…只是不一樣的是..他們會包遊覽車過來,本來一桌的菜要變三桌了…
小當家:我懷疑他們是來蹭飯的吧!!!


cover

認識 useCallback

基本上 useCallback 的目的就跟 useMemo 一樣,都是為了儲存記憶體的 Memorized Hook,只差在 useMemo 是回傳值,而 useCallback 是回傳 callback function,在 dependencies 沒有改變的情況下,把某個 function 保存下來,減少不必要的重新渲染


useCallback 使用方法

  • 從 react 中載入 useCallback 方法
  • useCallback 帶入參數,第一個參數為函式,第二個參數為 dependencies 陣列

1. 從 react 中載入 useCallback 方法

import { useCallback } from "react";

2. useCallback 帶入參數

const memoizedValue = useCallback (() => { doSomething(a) }, [a]);

第一個參數為函式,函式回傳不必要重新渲染的程式

第二個參數為 dependencies 陣列,useCallback 會依 dependencies 陣列去做比對差異,如果 dependencies 有變動,才會重新渲染,並回傳 callback function
如沒有 dependencies,則放空陣列,每次 render 時都會計算新的值。


useCallback 使用情境

  • 依賴本地 stateprops 來創建函數,需要使用到緩存函數的地方
  • useEffect 的 dependencies 為 function

Object、 Array 因為是 by reference ,所以使用 useMemo 包裝,而 useCallback 也是一樣意思, useCallback 是把function儲存起來再來做 function 的比對, function 也是 by reference,雖然是一樣的內容,但經過比對會是不一樣的 function

const functionOne = function() {
  return 1;
};
const functionTwo = function() {
  return 1;
};
console.log(functionOne === functionTwo); // false

[範例]

App.js

import { useState } from "react";
import Child from "./components/Child";

function App() {
  const [count, setCount] = useState(0);
  const [value, setValue] = useState("");

  const childCount = () => {
    return [count + 1, count + 2, count + 3];
  };

  return (
    <div>
      <h1>現在號碼:{count}</h1>

      <input
        value={value}
        onChange={(e) => {
          setValue(e.target.value);
        }}
        placeholder="無關緊要的 input"
      />

      <Child childCount={childCount} />

      <button
        onClick={() => {
          setCount((state) => state + 1);
        }}
      >
        下一位
      </button>
    </div>
  );
}

export default App;

components/Child.js

import { useEffect, useState } from "react";

const Child = (props) => {
  const { childCount } = props;

  const [count, setCount] = useState([]);

  useEffect(() => {
    console.log("render Child");
    setCount(childCount());
  }, [childCount]);

  return <h1>排隊號碼:{count.join(", ")}</h1>;
};

export default Child;

簡單說明情境:

  • 畫面有一個計數器,點擊按鈕 count 會 + 1
  • childCount 函式,會回傳 count 下三個數字的陣列
  • 導入顯示排隊號碼的 Child 組件,傳入 props (childCount 函式)
  • 一個無關緊要的 input,用來測試 Child 組件是否跟著 re-render

useCallback

codesandbox 程式碼範例

現在我們點擊下一位,也會很正常的渲染 Child 組件



但在輸入 input 時,發生了什麼事你應該也猜到了!/images/emoticon/emoticon13.gif



useCallback

沒錯!明明跟組件無關,但輸入 input 時也 render 了 Child 組件,/images/emoticon/emoticon04.gif

這時我們可以用 useCallback 來解決~

const childCount = useCallback(() => {
  return [count + 1, count + 2, count + 3];
}, [count]);

useCallback

codesandbox 程式碼範例

現在輸入 input 也不會影響到 Child 組件囉!用 useCallback 包住以確保 count 變動才會重新渲染


結語

useCallback 跟 useMemo 用法其實大同小異,
其實 useCallback(fn, deps) 就同等於 useMemo(() => fn, deps)

他們有相同的特性,當然同樣的也不要過度使用,最好是用在執行速度很慢、變動性不大的函式,以減少重新渲染的目的


本文將同步更新至我的部落格
Lala 的前端大補帖



上一篇
[DAY 18] useMemo 緩存記憶體,避免重新渲染
下一篇
[DAY 20] useRef 儲存資料與指定 DOM 元素
系列文
你 React 了嗎? 30 天解鎖 React 技能30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言