React(12) - useCallback & useMemo & memo


Posted by TempuraEngineer on 2022-11-17

目錄


memorized hook是甚麼

memorized hook接受2個參數,第一個是函式,第二個式依賴陣列(dependency array)

它會記住依賴陣列的狀態,當依賴陣列內的值更新時才會呼叫callback

因此常用於避免不必要的渲染、計算


useCallback

useCallback是個custom hook,它只能在functional component內使用

常用於記憶function,以避免組件內的function在組件被重新渲染時,function又被重新宣告(重新分配記憶體)

// 這兩個變數用於儲存舊的function
let m;
let n;

const App = () => {
  const [count, setCount] = useState(0)

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

  const normalFunction = () => {
    console.log('normal');
  };

  // 對比新舊function,確認是否來自同個記憶體位置
  console.log(`memoFunction is not change: ${memoFunction === m}`);
  console.log(`normalFunction is not change: ${normalFunction === n}`);

  return (
    <div className="App">
      <h1>count: {count}</h1>

      <button
        type="button"
        onClick={() => {
          let res = count + 1;
          setCount(res);
        }}
      >
        add
      </button>

      <button
        className="ml-3"
        type="button"
        onClick={() => {
          m = memoFunction
          n = normalFunction
        }}
      >
        set
      </button>
    </div>
  )
}


useMemo

useMemo是個custom hook,它只能在functional component內使用

常用於記憶function的回傳值(ex: 經過計算的值),以避免組件內的function在組件被重新渲染時,function被重新呼叫,故能避免不必要的重新計算衝擊效能

no useMemo

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

  const res = useMemo(() => {
    console.log('calculating');

    return count * 10;
  }, [count]);

  return (
    <div className="App">
      <h1>
        count: {count} / number: {number}
      </h1>
      <h2>count * 10 = {res}</h2>

      <button
        type="button"
        onClick={() => {
          setCount(count + 1);
        }}
      >
        add count
      </button>

      <button
        type="button"
        onClick={() => {
          setNumber(number + 1);
        }}
      >
        add number
      </button>
    </div>
  )
}


memo

(2023/2/10更新)

常用於記憶組件,被memo包住的組件在其父組件更新,但其props沒更新時不會重新渲染

和useCallback一起用能避免父組件state更新重新渲染時,props沒更新,但子組件卻重新渲染

const NormalChild = ({content}) => {
  console.log('NormalChild re-render');

  return (
    <div className="border-solid border-2 border-blue-400 p-2">
        <h1>{content}</h1>
    </div>
  )
}

const MemoizedChild = React.memo(({content}) => {
  console.log('MemoizedChild re-render');

  return (
    <div className="border-solid border-2 border-yellow-400 p-2">
      <h1>{content}</h1>
    </div>    
  )
});

const App = () => {
  const [count, setCount] = useState(0);

  const [memoContent, setMemoContent] = useState('memo');
  const [normalContent, setNormalContent] = useState('normal');

  return(
    <div>
      <h1>count: {count}</h1>

      <button
        type="button"
        onClick={() => {
          setCount(count + 1);
        }}
      >
        add count
      </button>

      <button
        type="button"
        onClick={() => {
          setCount(count + 1);
          setNormalContent('mmm');
          setNormalContent('nnn');
        }}
      >
        add count and set content
      </button>

      <div className='grid grid-cols-2 gap-2'>
        {/* num變,但是props沒變 */}

        {/* 使用memo,不會重新render */}
        <MemoizedChild content={memoContent} />

        {/* 會重新render */}
        <NormalChild content={normalContent} />
      </div>
    </div>
  );
}


比較表

useCallback useMemo memo
記憶 function function的回傳值 functional component
用在functional component內 必須 必須 不建議
compare 依賴陣列 依賴陣列 props的shallow compare


參考資料

React - useCallback
React - useMemo
React - memo
React 性能優化那件大事,使用 memo、useCallback、useMemo


#React #useMemo #useCallback #memo







Related Posts

淺談 React 專案的測試

淺談 React 專案的測試

【JS 大魔王 - 2】Hoisting 可以幹嘛?

【JS 大魔王 - 2】Hoisting 可以幹嘛?

關於 React 小書:評論功能(六):刪除評論、預防 XSS

關於 React 小書:評論功能(六):刪除評論、預防 XSS


Comments