本文へスキップ
BecomeCoder

Reactコース · 第6章 実アプリとエコシステム · レッスン19

再描画の最適化 ― useMemo・useCallback・React.memo

ブラウザで完結

導入

state が変わるとコンポーネントは再描画されます。ふつうは十分速いのですが、重い計算大量の子コンポーネントがあるアプリでは、「その state とは関係ない場所まで毎回計算し直す・再描画する」ことが目に見えて遅さにつながることがあります。useMemouseCallbackReact.memo は、そうした無駄を後から削るための道具です(最初から全部に付ける必要はありません)。

説明

// わざと時間のかかる計算にしている(本来はもっと重い処理を想定)
function slowSquare(n) {
  let result = 0;
  for (let i = 0; i < 3000000; i++) {
    result = n * n;
  }
  return result;
}

// React.memo … 渡された props が変わらない限り再描画をスキップする
const ChildButton = React.memo(function ChildButton({ onClick }) {
  console.log("ChildButton が再描画されました");
  return <button onClick={onClick}>押す(子コンポーネント)</button>;
});

function App() {
  const [number, setNumber] = React.useState(2);
  const [other, setOther] = React.useState(0);

  // useMemo … number が変わらない限り slowSquare を再実行しない
  const squared = React.useMemo(() => slowSquare(number), [number]);

  // useCallback … 中身が変わらない限り「同じ関数」を使い回す
  const handleClick = React.useCallback(() => {
    console.log("クリックされました");
  }, []);

  return (
    <div style={{ fontFamily: "system-ui", maxWidth: 320 }}>
      <p>{number} の2乗 = {squared}</p>
      <button onClick={() => setNumber((n) => n + 1)}>numberを増やす</button>
      <p>他の状態: {other}</p>
      <button onClick={() => setOther((o) => o + 1)}>otherだけ増やす</button>
      <ChildButton onClick={handleClick} />
    </div>
  );
}
  • React.useMemo(() => 計算, [依存]) … 依存配列の値が変わらない限り、前回の計算結果を使い回します。「otherだけ増やす」を押しても number は変わらないので slowSquare は再実行されません。
  • React.memo(Component) … 渡された props が(前回と)変わっていなければ、そのコンポーネントの再描画を丸ごとスキップします。
  • ただし関数をそのままインラインで渡す(onClick={() => ...})と、描画のたびに毎回新しい関数が作られるため、React.memo は「props が変わった」と判定してしまいます。React.useCallback(関数, [依存]) で関数そのものを使い回すことで、React.memo が効くようになります。
  • 最適化は「遅いと感じてから」で十分です。すべての値・関数に useMemo/useCallback を付けるのはやり過ぎで、かえって読みにくくなります。

やってみよう

React.useCallback をやめて handleClick をただの関数(const handleClick = () => { ... })に書き換え、「otherだけ増やす」を押すたびにコンソールへ ChildButton が再描画されました が出るようになることを確認しましょう。useCallback があるときは、そのログが最初の1回しか出ないはずです。

実際に動かしてみよう

下のエディタにReactのコードを書くと、右側のプレビューにその場で描画されます。`function App() { ... }` を定義すると自動で表示されます(React・useState などは React.useState の形で使えます)。本文の例を打ち込んだり書き換えたりして、表示の変化を確かめながら進めましょう。

React — ブラウザ内で実行

JSXをその場で変換して描画する学習用React環境(React18 + Babel・同梱)を読み込みます。
スクロールして表示された時点でも自動で読み込まれます。