useCallbackとuseMemoを使いこなすためのヒント:パフォーマンス向上のためのベストプラクティス
React.jsにおけるuseCallbackとuseMemoの実用的な違い
useCallback
useCallbackは、関数自体をキャッシュします。つまり、関数オブジェクトの参照が同じであれば、たとえ関数内の値が変わっていても、再レンダリング時に再実行されません。
- 頻繁に更新される値に基づいて別の関数を作成する場合
- カスタムフックで関数を返す場合
- 子コンポーネントにコールバック関数を渡す場合
const MyComponent = () => {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
// 高価な処理
const result = expensiveCalculation(count);
// 結果を更新
setCount(result);
}, [count]);
return (
<div>
<button onClick={handleClick}>クリック</button>
</div>
);
};
この例では、handleClick
関数はcount
の値に基づいて計算を行うため、count
が更新されるたびに再実行されます。しかし、useCallback
を使うことで、count
が変わっても関数オブジェクト自体はキャッシュされるため、実際の再実行は必要最低限に抑えられます。
useMemo
useMemoは、関数の呼び出し結果をキャッシュします。つまり、関数内の値が変わらなければ、再レンダリング時に再実行されません。
useMemoの使い所
- レンダリングに影響を与えない値を計算する場合
- 高価な計算を行う関数
const MyComponent = () => {
const [count, setCount] = useState(0);
const memoizedValue = useMemo(() => {
// 高価な処理
const result = expensiveCalculation(count);
return result;
}, [count]);
return (
<div>
{memoizedValue}
</div>
);
};
この例では、expensiveCalculation
関数はcount
の値に基づいて計算を行うため、count
が更新されるたびに再実行されます。しかし、useMemo
を使うことで、計算結果がキャッシュされるため、実際の計算は必要最低限に抑えられます。
useCallbackとuseMemoの比較
機能 | useCallback | useMemo |
---|---|---|
キャッシュ対象 | 関数自体 | 関数の呼び出し結果 |
再実行条件 | 関数オブジェクトの参照が変わ | 関数内の値が変わ |
典型的な使用例 | コールバック関数、カスタムフック | 高価な計算、頻繁に更新される値 |
const ParentComponent = () => {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
// 親コンポーネントの `count` を更新
setCount(count + 1);
}, []);
return (
<div>
<ChildComponent handleClick={handleClick} />
</div>
);
};
const ChildComponent = ({ handleClick }) => {
return (
<div>
<button onClick={handleClick}>クリック</button>
</div>
);
};
このコードでは、ParentComponent
はChildComponent
にhandleClick
というコールバック関数を渡しています。handleClick
関数は、ParentComponent
のcount
を更新します。
useCallbackを使わなかった場合、ParentComponent
が再レンダリングされるたびに、ChildComponent
に渡されるhandleClick
関数は新しい関数オブジェクトになります。
しかし、useCallbackを使うことで、count
が変わっても関数オブジェクト自体はキャッシュされるため、ChildComponent
に渡されるhandleClick
関数は常に同じ関数オブジェクトになります。
const MyComponent = () => {
const [count, setCount] = useState(0);
const memoizedValue = useMemo(() => {
// 高価な処理
const result = expensiveCalculation(count);
return result;
}, [count]);
return (
<div>
{memoizedValue}
</div>
);
};
このコードでは、expensiveCalculation
関数はcount
の値に基づいて計算を行うため、count
が更新されるたびに再実行されます。
しかし、useMemo
を使うことで、計算結果がキャッシュされるため、実際の計算は必要最低限に抑えられます。
機能 | useCallback | useMemo |
---|---|---|
キャッシュ対象 | 関数自体 | 関数の呼び出し結果 |
再実行条件 | 関数オブジェクトの参照が変わ | 関数内の値が変わ |
典型的な使用例 | コールバック関数、カスタムフック | 高価な計算、頻繁に更新される値 |
shouldComponentUpdate
は、コンポーネントが更新される必要があるかどうかを判断するライフサイクルメソッドです。このメソッドをオーバーライドすることで、不要な再レンダリングを抑制することができます。
const MyComponent = () => {
const [count, setCount] = useState(0);
shouldComponentUpdate(nextProps, nextState) {
// propsとstateの変化をチェック
if (this.props.count === nextProps.count) {
return false;
}
return true;
}
return (
<div>
{count}
</div>
);
};
この例では、count
以外のpropsやstateが変化した場合のみ、コンポーネントを更新するようにしています。
PureComponent
PureComponent
は、shouldComponentUpdate
をデフォルトで実装しているReact.jsのコンポーネントです。PureComponent
は、props
とstate
の浅い比較を行い、変化がない場合は再レンダリングを抑制します。
class MyComponent extends React.PureComponent {
render() {
const { count } = this.props;
return (
<div>
{count}
</div>
);
}
}
この例では、MyComponent
はPureComponent
を継承しているので、props
とstate
の浅い比較に基づいて再レンダリングが抑制されます。
memo
memo
は、React.js v16.6で導入された高階コンポーネントです。memo
は、コンポーネントをラップして、props
の変化に基づいて再レンダリングを抑制します。
const MyComponent = ({ count }) => {
return (
<div>
{count}
</div>
);
};
const MemoizedComponent = memo(MyComponent);
この例では、MyComponent
をmemo
でラップすることで、count
以外のpropsの変化では再レンダリングが抑制されます。
useRef
useRef
は、レンダリング間で値を保持するために使用できるフックです。useRef
を使って、高価な計算結果を保持することができます。
const MyComponent = () => {
const [count, setCount] = useState(0);
const memoizedValue = useRef(null);
useEffect(() => {
// 高価な処理
const result = expensiveCalculation(count);
memoizedValue.current = result;
}, [count]);
return (
<div>
{memoizedValue.current}
</div>
);
};
この例では、useRef
を使って、expensiveCalculation
関数の結果をmemoizedValue
という変数に保持しています。count
が更新された場合のみ、expensiveCalculation
関数が実行されます。
reactjs