
Reactのメモ化とは
コンポーネントや関数の「計算結果」を記憶して、再利用する仕組みのことです。
コンポーネントのメモ化には「memo」、関数のメモ化には「useCallback, useMemo」が使用されます。
※ Reactを使用したことがある人なら知らない人はいないので、メモ化の詳しい説明は割愛します。
Reactのメモ化は手動でやると結構面倒な作業です。
しかも、メモ化は「memo, useCallback, useMemo」を追加することでコードの可読性を低下させます。
例えば以下のようなコードがあったとします。
// メモ化していないコード📝
import { useState } from 'react'
// 重い計算をする関数
const expensiveCalculation = (num: number) => {
let result = 0
for (let i = 0; i < 1000000000; i++) {
result += num * i
}
return result
}
// 重たい計算を行うコンポーネント
const ExpensiveComponent = ({ num }: { num: number }) => {
const result = expensiveCalculation(num)
return <div>計算結果: {result}</div>
}
const App = () => {
const [count, setCount] = useState(0)
const [num, setNum] = useState(5)
const handleClick = () => {
setCount(count + 1)
}
const doubledNum = num * 2
return (
<div>
<h1>Reactメモ化なしのサンプル</h1>
<button onClick={handleClick}>カウント: {count}</button>
<p>数値の2倍: {doubledNum}</p>
<input
type="number"
value={num}
onChange={(e) => setNum(Number(e.target.value))}
/>
<ExpensiveComponent num={num} />
</div>
)
}
export default App
これをメモ化すると以下のようなコードになります。
メモ化のためのコードが追加されて、可読性が低下していることがわかります。
// メモ化したコード
import { useState, useMemo, useCallback } from 'react'
// 重い計算をする関数
const expensiveCalculation = (num: number) => {
let result = 0
for (let i = 0; i < 1000000000; i++) {
result += num * i
}
return result
}
// 重たい計算を行うコンポーネント
const ExpensiveComponent = ({ num }: { num: number }) => {
const result = useMemo(() => expensiveCalculation(num), [num])
return <div>計算結果: {result}</div>
}
const App = () => {
const [count, setCount] = useState(0)
const [num, setNum] = useState(5)
const handleClick = useCallback(() => {
setCount(count + 1)
}, [count])
const doubledNum = useMemo(() => num * 2, [num])
return (
<div>
<h1>Reactメモ化のサンプル</h1>
<button onClick={handleClick}>カウント: {count}</button>
<p>数値の2倍: {doubledNum}</p>
<input
type="number"
value={num}
onChange={(e) => setNum(Number(e.target.value))}
/>
<ExpensiveComponent num={num} />
</div>
)
}
export default App
※ この例だとonChangeのときのvalue値変更後の処理は重いままなので、localStorageを併用したほうが良いです。
React 17と18のメモ化の自動化
React 17または18でもbabel-plugin-react-compilerとreact-compiler-runtimeをインストールすれば、メモ化の自動化が可能になります。
メモ化の自動化を試すためにReact 17をインストールしてみます。
React 18の場合は以下の「17」を「18」に変えて、main.tsxは変更しなくてOKです。
npm create vite@latest my-react-17 -- --template react-ts
cd my-react-17
npm un react react-dom
npm i react@17 react-dom@17
npm i -D babel-plugin-react-compiler react-compiler-runtime
React 17でcreateRootは使用不可なので、main.tsxを以下のコードに書き換えます。
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import './index.css'
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
)
次にvite.config.tsをbabel-plugin-react-compilerを読み込む設定に変更します。
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
const ReactCompilerConfig = {
target: '17'
}
export default defineConfig({
plugins: [
react({
babel: {
plugins: [
['babel-plugin-react-compiler', ReactCompilerConfig]
],
},
}),
],
})
これらの設定が終わったら、App.tsxを「メモ化していないコード📝」に書き換えて確認してみてください。
「memo, useCallback, useMemo」を追加していませんが、メモ化が自動で行われているため、「カウント」のボタンを押しても動作が重くなっていないことが確認できます。
React 18でも同様の結果になります。