4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【React】メモ化は最終手段!? ステート設計の見直しとrAF, throttleの利用でブラウザ負荷を削減

4
Last updated at Posted at 2025-12-02

対象読者

Reactのメモ化は知っているけど、他のパフォーマンス改善方法も学びたい方

はじめに

Reactのパフォーマンス改善について調べると、useMemouseCallback といったメモ化テクニックがよく紹介されます。しかし、メモ化は最終手段のケースがあります。

本記事は、こちらの動画(How to Prevent Unnecessary Re-Renders in React)の内容紹介 + αとなります。実践的なパフォーマンス改善手法を紹介します。

元動画はReact公式ドキュメントよりも丁寧な解説で、すぐに効果が出る内容なので強くおすすめします!

問題の本質:不要な再レンダーはどこから来るのか?

問題のパターン

親コンポーネントのスタイル更新時に、子コンポーネントが毎秒数十回も再レンダーされるケース (mousemove 発火など)があります。

function ParentComponent() {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  const [childData, setChildData] = useState('...');

  const handleMouseMove = (e) => {
    // マウス移動のたびにstateを更新
    setPosition({ x: e.clientX, y: e.clientY });
  };

  return (
    <div onMouseMove={handleMouseMove}>
      <div style={{ transform: `translate(${position.x}px, ${position.y}px)` }}>
        追従する要素
      </div>
      
      {/* 激重コンポーネント */}
      {/* マウス移動のたびに不要な再レンダー発生! */}
      <VeryHeavy data={childData} /> 
    </div>
  );
}

この場合、positionの変更に関係ないVeryHeavyまで再レンダーされてしまいます。

解決策1:親をラッパー化し、親子のstateを分離

stateで管理する場合は、state管理の責務を分離します。

Before(悪い例)

import { useState } from "react"
import "./App.css"

function App() {
  // `setCount` するたびに `App` 全体が再描画
  const [count, setCount] = useState(0)
  
  return (
    <>
      <div onClick={() => setCount((count) => count + 1)}>
          <div> count is {count} </div>
          
          <VeryHeavy>
            描画がとても遅い、激重コンポーネントだと思ってください
          </VeryHeavy>
      </div>
    </>
  )
}

After(良い例)

import { useState } from "react"
import "./App.css"

function App() {
    const Counter = ({ children }) => {
        // 依存 state を利用コンポーネント内に閉じ込める 
        const [count, setCount] = useState(0)

        return (
            <div onClick={() => setCount((count) => count + 1)}>
                <div> count is {count} </div>

                {children}
            </div>
        )
    }

  // VeryHeavy の state が変わらない場合は再描画されない
  return (
    <>
        <Counter>
            <VeryHeavy>
                描画がとても遅い、激重コンポーネントだと思ってください
            </VeryHeavy>
        </Counter>
    </>
  )
}

ポイント

  • 子コンポーネントは自身のstate変更時のみ再レンダー
  • 親のstateと子のstateが完全に分離
  • 依存していないstateがコンポーネントに混在していないかチェック

解決策2: 高頻度イベントの最適化

mousemovescroll などの高頻度イベントには、requestAnimationFramethrottle を組み合わせることで負荷を削減できます。

2つの最適化ポイント

項目 requestAnimationFrame throttle
実行タイミング ブラウザの描画サイクルに同期

高負荷時は自動で頻度を落としてくれる

高リフレッシュレートのディスプレイでは自動で描画が増え、より滑らかに
実行間隔の指定。そもそもの実行を減らす
バックグラウンド時 自動的に実行を抑制 通常通り実行

実装例

function ParentComponent() {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  const rafIdRef = useRef(null);

  const handleMouseMove = throttle((e) => { // lodash の throttle 等
    if (rafIdRef.current) {
      cancelAnimationFrame(rafIdRef.current);
    }
    rafIdRef.current = requestAnimationFrame(() => {
      setPosition({ x: e.clientX, y: e.clientY });
    });
  }, 32); // 約30fps(60fpsの半分)

  return <div onMouseMove={handleMouseMove}>{/* ... */}</div>;
}

パフォーマンス改善の優先順位

  1. Local stateをちゃんとlocalに保つ

    • 依存していないstateがコンポーネントにないか確認
    • 必要に応じてコンポーネントを分割
  2. 高頻度イベントの最適化

    • requestAnimationFramethrottle を活用
    • イベントの発火頻度を制御
  3. 上記で解決できない場合メモ化

    • useMemo, useCallback

再レンダーを可視化する最強ツール:React Scan

パフォーマンス問題を特定するには、どこで不要な再レンダーが起きているかを可視化することが重要です。

React Scan

↓ レンダーされた要素がハイライトされる
image.png

冒頭で紹介した How to Prevent Unnecessary Re-Renders in React でもおすすめされており、実際に使ってみるとどのコンポーネントが再レンダーされているかが一目瞭然です!

結果

これらのテクニックを使った結果:

  • 不要な子コンポーネントの再レンダーがゼロに
  • ブラウザの状態に合わせて処理回数を自動で最適化

パフォーマンスが改善しました。

まとめ

  • メモ化が改善に寄与しないケースもある。まずはstate管理の設計を見直す
  • 依存していないstateがコンポーネントに混在していないか常に意識
  • 高頻度イベントには requestAnimationFramethrottle を活用
  • React Scanで再レンダーを可視化して問題を特定

現場で即効果が出るのでぜひ試してみてください!

余談

元動画 のコメント欄にある投稿者Daniel Noworyataさんの返信が素敵です。技術への優しい眼差しを感じます。
image.png

最善策:Reactを使わないこと。

それでも最近はReactに関する悪口が多すぎると思うよ :)それでもプロジェクトで使う価値はある。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?