第12回:Reactのレンダリング最適化:useMemoとuseCallbackで無駄な再描画を防ぐ

lang

前回:useEffect入門:API連携やタイマー処理など「副次作用」を操る方法では、実行タイミングをコントロールする「副作用」の扱いを学びました。

Reactの素晴らしい点は、データが変われば画面を「自動で」書き換えてくれることです。しかし、アプリが複雑になると、**「本当は書き換えなくていい場所まで、何度も計算し直して重くなる」**という問題が発生します。

2026年現在のReact開発において、アプリを爆速に保つための「賢いサボり方」である useMemouseCallback を攻略しましょう!


1. なぜ「最適化」が必要なのか?

Reactコンポーネントは、State(状態)が更新されるたびに、関数の中身を上から下まで全部実行し直します。

  • 問題1: 毎回「ものすごく重い計算」をやり直すと、動作がカクつく。
  • 問題2: 中身が同じ「関数」なのに、再描画のたびに「別物」だと判定されて、子コンポーネントまで連鎖的に描き直されてしまう。

これを防ぐのが、計算結果や関数を「メモ(保存)」しておく仕組みです。


2. useMemo と useCallback の違い

似ていますが、保存する対象が違います。

フック名保存するもの使い所
useMemo計算結果(値)複雑な計算を使い回したい時
useCallback関数自体関数を子コンポーネントに渡す時

どちらも、第11回で学んだ useEffect と同じように 「依存配列」 を持ち、「この値が変わった時だけ作り直してね」という指示を出します。


3. 【実践】重い計算を「サボらせる」サンプル

今回は、数字を入力するたびに発生する「重い計算」を useMemo で節約する例を見てみましょう。

第2回の環境で作成した App.jsx を書き換えてください。

App.jsx(最小構成版)

JavaScript

import React, { useState, useMemo } from 'react';

export default function App() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState("");

  // 1. 重い計算のシミュレーション
  // useMemo を使わないと、textを入力するたびにこのループが走り、重くなります
  const expensiveCalculation = useMemo(() => {
    console.log("重い計算を実行中...");
    let sum = 0;
    for (let i = 0; i < 100000000; i++) { sum += i; } // 擬似的な重い処理
    return sum + count;
  }, [count]); // count が変わった時だけ再計算する!

  return (
    <div style={{ textAlign: 'center', marginTop: '50px' }}>
      <h1>パフォーマンス最適化</h1>
      
      <p>重い計算の結果: {expensiveCalculation}</p>
      
      <button onClick={() => setCount(count + 1)}>
        カウントアップ(計算をやり直す)
      </button>

      <div style={{ marginTop: '20px' }}>
        <input 
          type="text" 
          value={text} 
          onChange={(e) => setText(e.target.value)} 
          placeholder="ここに入力しても計算は走りません"
        />
        <p>入力中の文字: {text}</p>
      </div>
    </div>
  );
}

CodeSandboxでuseMemoの最適化を体験する


4. コードの解説(JavaScript & React)

JavaScriptのポイント

  • ループ処理 (for):1億回の足し算を行っています。これは現代のPCでも目に見えて時間がかかる処理です。
  • 関数の等価性:JavaScriptでは、見た目が同じ関数 () => {} でも、新しく定義されるたびに「メモリ上の住所」が変わるため、Reactは「別の関数になった」と判断します。これを防ぐのが useCallback の役割です。

Reactのポイント

  • メモ化(Memoization):前回の結果をメモリに保存しておき、必要なときだけ再計算する手法です。
  • 依存配列の魔法:[count] と指定することで、text(入力欄)が変わっても計算をスキップし、前回の結果を使い回しています。

5. 2026年の学び:やりすぎ厳禁!

2026年現在のReact開発において、全ての場所を useMemo で囲むのは推奨されません。

  • 理由: メモ化自体にもメモリを消費する「コスト」があるからです。
  • 指針: 「明らかに動作が重い」と感じた時や、巨大なリストを表示する時に導入するのがベストです。

まずは「Reactは基本、全部描き直す」「重い時は useMemo でサボらせる」という感覚を掴んでおきましょう。


今回のまとめ

  1. useMemo は「計算結果」を保存し、無駄な計算を防ぐ。
  2. useCallback は「関数」を保存し、子への無駄な伝播を防ぐ。
  3. どちらも 依存配列 で更新タイミングを決める。
  4. 何でもかんでもメモ化せず、パフォーマンスの問題が出てから使うのがプロのコツ。

次回予告:DOMを直接触りたい時は?

これまでは「Reactにお任せ」で画面を書き換えてきました。

しかし、特定の入力欄に「自動でフォーカスを当てる」とか「スクロール位置を操作する」など、Reactの枠を超えて直接HTML(DOM)を触りたい瞬間があります。

次回、「第13回:useRefの使い方:DOM要素への直接アクセスと値を保持するテクニック」

Reactのルールを守りつつ、裏口からこっそり操作する「忍者のようなフック」を伝授します。お楽しみに!


本日の宿題:

サンプルコードの useMemo を外して、ただの関数として実行するように書き換えてみてください。入力欄(text)に文字を打つとき、動作が重くなるのを感じられますか?(※PCの性能によっては一瞬で終わるかもしれませんが、コンソールのログを見て実行回数を確認してみましょう!)

コメント

タイトルとURLをコピーしました