useCallback とは
メモ化されたコールバックを返します。
useMemo とは
メモ化された値を返します。
わかるようなわかないような感じだったので挙動を確認するコードを書きました。
コード
import * as React from "react";
import { useCallback, useMemo, useState } from "react";
let tmpHandleClick: () => void;
let tmpHandleClick2: () => void;
export const Component: React.FC = () => {
const [count, setCount] = useState(0);
const double = useMemo(() => {
console.log("double が計算された。");
return count * 2;
}, [count]);
const handleClick = useCallback(() => {
setCount(prev => prev + 1);
}, []);
if (tmpHandleClick !== handleClick) {
console.log("handleClick が生成された。");
}
tmpHandleClick = handleClick;
// ----------------------
const [count2, setCount2] = useState(0);
const dobule2Func = (i: number) => {
console.log("double2 が計算された。");
return i * 2;
};
const double2 = dobule2Func(count2);
const handleClick2 = () => {
setCount2(count2 + 1);
};
if (tmpHandleClick2 !== handleClick2) {
console.log("handleClick2 が生成された。");
}
tmpHandleClick2 = handleClick2;
return (
<div>
<p>state: {count}</p>
<p>double: {double}</p>
<button onClick={handleClick}>handleClick</button>
<br />
<p>state2: {count2}</p>
<p>double2: {double2}</p>
<button onClick={handleClick2}>handleClick2</button>
</div>
);
};
動かしてみた結果
初期表示時
初回なのですべてのログが表示される。
handleClick 押下時
handleClick は新たに生成されない。
handleClick は useCallbackの第2引数に空配列を与えているので初期表示時にのみ生成される。
useCallback は第2引数の配列に含まれる値に変化があった時のみ関数を新しく生成するため。
handleClick2 押下時
useMemo の 第2引数に渡している count に変更がないため double は計算されない。
まとめ
react の component が render されると、毎回、component の中身がすべて再実行されるが、
useMemo, useCallback を使うと必要ない再計算、再生成が行われない。
という当たり前のことが実際に動かしたことで腹落ちしました。