第8回

useMemoでパフォーマンス最適化:重い計算をメモ化する

ReactのuseMemoを解説します。メモ化の仕組み・高コスト計算の最適化・参照の安定化、そしてuseMemoを使うべき場面・使わなくてよい場面の判断基準を実例付きで学べます。

·5分で読める

制作・運営:React Guide 運営者

計算結果を再利用できる条件

useMemo は、依存が前回と同じなら計算結果を再利用するための最適化です。stateやpropsの代わりではありません。外しても正しく動作するコードに使います。

フィルター文字を変えるたびに再計算が必要なら、その更新の計算量はuseMemoだけでは減りません。無関係な入力による再計算を省けるかを分けて考えます。

無関係な入力と検索を分けて試す

以下を src/App.tsx に置きます。大量のデータや外部APIを使わず、再計算する条件を確認する例です。速度改善の実測例ではありません。

import { useMemo, useState } from 'react'

const products = [
  { id: 'pen', name: '青いペン' },
  { id: 'note', name: '青いノート' },
  { id: 'bag', name: '赤いかばん' },
]

export default function App() {
  const [query, setQuery] = useState('')
  const [note, setNote] = useState('')
  const filtered = useMemo(() => {
    console.log('商品を絞り込み')
    return products.filter(p => p.name.includes(query))
  }, [query])

  return (
    <main>
      <label>検索<input value={query} onChange={e => setQuery(e.target.value)} /></label>
      <label>メモ<input value={note} onChange={e => setNote(e.target.value)} /></label>
      <ul>{filtered.map(p => <li key={p.id}>{p.name}</li>)}</ul>
    </main>
  )
}

productsはモジュールで定義した固定データなので、変化する依存はqueryだけです。productsをpropsで受け取るなら [products, query] にします。noteの更新では同じ結果を使い、queryの更新では絞り直します。

StrictModeでは計算関数を追加で実行する場合があります。調査用ログを除き、計算関数は副作用を持たせず純粋にしてください。この3件程度の検索では、実際にはuseMemoなしで十分です。

sortは元の配列を変更する

// コンポーネント内の抜粋。recordsはprops。
const sorted = useMemo(
  () => [...records].sort((a, b) => b.amount - a.amount),
  [records],
)

records.sort(...) はpropsそのものを変更します。コピーしてから並べ替える点は、メモ化の有無にかかわらず必要です。親が毎回新しいrecords配列を渡せば、内容が同じでも再計算されます。

子の再レンダーを省く用途

memoで包んだ子へ配列を渡す場合も、同じ配列を再利用することが役立ちます。ただし、他のpropsが変わる、子自身のstateが変わる、購読するContextが変わる場合には、子は再レンダーされます。

Effectのためだけにオブジェクトを固定したいなら、先にEffect内でオブジェクトを作れないか考えます。キャッシュが維持されることを、接続の正しさの条件にしないでください。

使うか決める手順

  1. 同じデータ・端末・操作で、遅い処理をProfilerなどで測ります。
  2. 計算の入力が変わらない更新が多いかを確認します。
  3. useMemoを入れて同じ条件で比べます。値が毎回変わるならキャッシュの効果は小さくなります。

軽い計算では管理コストや読みづらさが増えることがあります。一律に速くなる、遅くなるとは決めません。React Compilerを有効にしたプロジェクトでは自動メモ化もあるため、手書きの追加前に適用状況を確認します。

React公式:useMemo、memo

#react#hooks#useMemo#performance#intermediate