useMemo と React.memo の違い
- useMemo:値をメモ化
- React.memo:コンポーネントをメモ化
メモ化はキャッシュのこと。
キャッシュ化は、値を再計算せずに結果だけをどこかに保存しておいて、必要になったらそれを使うことができます。
一方、Reactメモはコンポーネントのみメモ化を行う。
ここが大きな違いで、useMemoはあらゆる値をメモ化しておくことができるので、jsxのコードの部分をメモ化することが可能。
検証
親コンポーネント
import React, { useCallback, useState } from "react";
import Child from "./Child";
const Example = () => {
console.log("Parent render");
const [countA, setCountA] = useState(0);
const [countB, setCountB] = useState(0);
const clickHandler = useCallback(() => {
setCountB(countB + 1);
},[countB]);
return (
<div className="parent">
<div>
<h3>親コンポーネント領域</h3>
<div>
<button
onClick={() => {
setCountA((pre) => pre + 1);
}}
>
ボタンA
</button>
<span>親のstateを更新</span>
</div>
</div>
<div>
<p>ボタンAクリック回数:{countA}</p>
</div>
<Child countB={countB} onClick={clickHandler}/>
</div>
);
};
export default Example;
React.memo を使用した子コンポーネント
import React from "react";
const Child = React.memo(({ countB, onClick }) => {
console.log("%cChild render", "color: red;");
return (
<div className="child">
<h2>子コンポーネント領域</h2>
<button onClick={onClick}>ボタンB</button>
<p>ボタンBクリック回数:{countB}</p>
</div>
);
});
export default Child;
useMemo を使用した子コンポーネント
第二引数に依存配列を設定
import React from "react";
const Child = ({ countB, onClick }) => {
console.log("%cChild render", "color: red;");
useMemo(() => {
console.log("%cuseMemo", "color: green;")
return (
<div className="child">
<h2>子コンポーネント領域</h2>
<button onClick={onClick}>ボタンB</button>
<p>ボタンBクリック回数:{countB}</p>
</div>
);
},[countB]);
};
export default Child;
- countBが更新された場合には、新しいjsxが返ってくる。
- それ以外にはメモ化されたjsxが返ってくる。
ボタンAを押したとき、useMemoのconsole.logは表示されない。
- 注意点:Childコンポーネント自体はレンダーされている。
- useMemoでメモ化した値がChildコンポーネントの実行結果として返ってきている。
関数を依存配列に含めるケースと含めないケース
関数の中の処理がなんらかのstateに依存している場合は依存配列の中に含めて書く。
import React from "react";
const Child = ({ countB, onClick }) => {
console.log("%cChild render", "color: red;");
useMemo(() => {
return (
<div className="child">
<h2>子コンポーネント領域</h2>
<button onClick={onClick}>ボタンB</button>
{/* <p>ボタンBクリック回数:{countB}</p> */} // 依存関係を一旦コメントアウト
</div>
);
},[onClick]); //依存配列をonClick(関数)にする
};
export default Child;
親コンポーネント
import React, { useCallback, useState } from "react";
import Child from "./Child";
const Example = () => {
console.log("Parent render");
const [countA, setCountA] = useState(0);
const [countB, setCountB] = useState(0);
const clickHandler = useCallback(() => {
console.log('countB',countB) //onClickイベントの中で現在のstateの状態をコンソールに追加
setCountB(countB + 1);
},[countB]);
return (
<div className="parent">
<div>
<h3>親コンポーネント領域</h3>
<div>
<button
onClick={() => {
setCountA((pre) => pre + 1);
}}
>
ボタンA
</button>
<span>親のstateを更新</span>
</div>
</div>
<div>
<p>ボタンAクリック回数:{countA}</p>
</div>
<Child countB={countB} onClick={clickHandler}/>
</div>
);
};
export default Example;
カウントが依存配列に含められているので、カウントのstateが更新されると、まずドラッグした関数が新しい関数で定義される。
const clickHandler = useCallback(() => {
console.log('countB',countB) //onClickイベントの中で現在のstateの状態をコンソールに追加
setCountB(countB + 1);
},[countB]);
それがclickHandler関数に入って、propsのonClickで子コンポーネントに渡される。
子コンポーネントのuseMemoの依存配列にも入っているので、onClickには、新しく定義され直した関数が格納されていることになる。
この状態だとコンソールでのカウントの値はインクリメントされていく。
import React from "react";
const Child = ({ countB, onClick }) => {
console.log("%cChild render", "color: red;");
useMemo(() => {
return (
<div className="child">
<h2>子コンポーネント領域</h2>
<button onClick={onClick}>ボタンB</button>
{/* <p>ボタンBクリック回数:{countB}</p> */} // 依存関係を一旦コメントアウト
</div>
);
},[]); //依存配列を削除
};
export default Child;
カウントBは0のまま。
クリックハンドラーの中は、countBに依存している状態になるので、countBの値が変更されたときに新しく関数を定義しないと、元のcountBを使って処理が行われている状態なので、うまく動かない。

