Reactアプリを作っていると、こんな経験をしたことはありませんか?
- アプリの動作がなんだか重くなってきた…
- 再レンダリングの度に関数が実行される…
React.memo
が効かない…
こうした問題を解決してくれるのが、ReactのuseMemo
です。
この記事では、Reactの『useMemo』について、以下の内容をサンプルコードを用いてわかりやすく解説します。
useMemo
とは?useMemo
を使わなかった場合のサンプルコードuseMemo
を使った場合のサンプルコード
useMemo
でオブジェクトや配列などの「参照型の値」をメモ化する場合useMemo
とuseCallback
の違い
useMemoとは?
useMemo
は、関数の実行結果(値)をメモ化(キャッシュ)するReactフックの一つです。
const memoizedValue = useMemo(() => {
// 時間のかかる処理
return 処理結果;
}, [依存する値]);
上記のように使うことで、依存する値が変わらない限り、関数の処理は再実行されずに前回の値が再利用されます(依存する値が変化した場合のみ関数が実行されます)。
補足
依存する値を空([]
)にした場合、関数は初回実行された後は二度と実行されません。
useMemo
は、オブジェクトや配列などの「参照型の値」もメモ化できるため、再レンダリング対策として使われることも多いです(詳細は後述)。
useMemoを使わなかった場合
まず、useMemo
を使わないサンプルコードを見てみましょう。
import { useState } from 'react';
function heavyCalculation(num) {
console.log('重たい処理実行中...');
let result = 0;
for (let i = 0; i < 1e9; i++) {
result += num;
}
return result;
}
export default function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// 毎回実行される!
const result = heavyCalculation(count);
return (
<>
<h1>結果: {result}</h1>
<button onClick={() => setCount(count + 1)}>+1</button>
<input value={text} onChange={(e) => setText(e.target.value)} />
</>
);
}
上記のサンプルコードでは、テキスト入力欄に1文字入力するだけで、親コンポーネント(App
)が再レンダリングされるので、heavyCalculation
関数という重たい処理が毎回実行されてしまいます。
useMemoを使った場合
次に、useMemo
を使った場合のサンプルコードを見てみましょう。
import { useMemo, useState } from 'react';
function heavyCalculation(num) {
console.log('重たい処理実行中...');
let result = 0;
for (let i = 0; i < 1e9; i++) {
result += num;
}
return result;
}
export default function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// useMemoで値をキャッシュ化
const result = useMemo(() => heavyCalculation(count), [count]);
return (
<>
<h1>結果: {result}</h1>
<button onClick={() => setCount(count + 1)}>+1</button>
<input value={text} onChange={(e) => setText(e.target.value)} />
</>
);
}
上記のサンプルコードでは、テキスト入力欄に1文字入力するだけで、親コンポーネント(App
)が再レンダリングされますが、useMemo
の依存する値(count
)は変わらないので、heavyCalculation()
は再実行されなくなり、パフォーマンスが大幅に改善します。
オブジェクトや配列などの「参照型の値」をメモ化する場合
useMemo
を使わないサンプルコードを見てみましょう。
import { memo, useState } from 'react';
const Child = memo(({ user }) => {
console.log('Childが再レンダリングされました');
return <div>こんにちは、{user.name}さん!</div>;
});
export default function App() {
const [count, setCount] = useState(0);
// 毎回新しいオブジェクトになる
const user = { name: '太郎' };
return (
<div>
<h1>カウント: {count}</h1>
<button onClick={() => setCount(count + 1)}>+1</button>
<Child user={user} />
</div>
);
}
上記のサンプルコードでは、親コンポーネント(App
)が再レンダリングされるたびに、user
オブジェクトが毎回新しく作られます。そのため、子コンポーネント(Child
)にとっては「props
(user
)の値が毎回変わっている」と判断されます。よって、memo
関数を使っていても子コンポーネント(Child
)は毎回再レンダリングされてしまうのです。
useMemo
を使ってオブジェクトの参照を固定した際のサンプルコードを以下に示します。
import { memo, useMemo, useState } from 'react';
const Child = memo(({ user }) => {
console.log('Childが再レンダリングされました');
return <div>こんにちは、{user.name}さん!</div>;
});
export default function App() {
const [count, setCount] = useState(0);
// useMemoでオブジェクトの参照を固定
const user = useMemo(() => ({ name: '太郎' }), []);
return (
<div>
<h1>カウント: {count}</h1>
<button onClick={() => setCount(count + 1)}>+1</button>
<Child user={user} />
</div>
);
}
useMemo
を使うことで、親コンポーネント(App
)が再レンダリングされても、user
オブジェクトの参照が毎回変わらないため、子コンポーネント(Child
)から見ると「props
が前回と同じ」と判断できます。その結果、memo
関数が効いて、子コンポーネント(Child
)の無駄な再レンダリングが防げます。
あわせて読みたい
Reactの『memo関数』については下記の記事で詳しく説明しています。興味のある方は下記のリンクからぜひチェックをしてみてください。 続きを見る【React】memo関数とは?使い方と効果をわかりやすく解説!
useMemoとuseCallbackの違い
ReactにはuseMemo
と似たような役割を持つuseCallback
というReactフックがあります。
どちらも「メモ化(キャッシュ)」に関係していますが、目的とメモ化する対象が異なります。以下に違いを示します。
フック | メモ化する対象 | 主な用途 |
---|---|---|
useMemo | 関数の実行結果(値) | 計算コストの高い処理の結果や、オブジェクトや配列の再生成を防ぎたいとき |
useCallback | 関数そのもの(参照) | 子コンポーネントに渡す関数の再生成を防ぎたいとき |
再レンダリングのたびに毎回重い処理が実行されたり、同じオブジェクト・配列を作るのがムダに感じたらuseMemo
を使いましょう。再レンダリングのたびに毎回同じ関数が再生成されるのがムダに感じたらuseCallback
を使いましょう。
あわせて読みたい
Reactの『useCallback』については下記の記事で詳しく説明しています。興味のある方は下記のリンクからぜひチェックをしてみてください。 続きを見る【React】useCallbackの使い方を分かりやすく解説!
本記事のまとめ
この記事では、Reactの『useMemo』について、以下の内容を説明しました。
useMemo
とは?useMemo
を使わなかった場合のサンプルコードuseMemo
を使った場合のサンプルコード
useMemo
でオブジェクトや配列などの「参照型の値」をメモ化する場合useMemo
とuseCallback
の違い
お読み頂きありがとうございました。