🛠️
React.memoの浅い比較について
React.memoの浅い比較について
浅い比較の仕組み
-
プリミティブ型の場合は、値そのものを比較します。例えば、
count={1}
からcount={1}
のように同じ値の場合、React.memo
は再レンダリングをスキップします。 -
オブジェクト型や配列型の場合、値ではなく**参照(メモリ上の位置)**を比較します。そのため、
props
の中にオブジェクトや配列が含まれている場合、たとえ内容が同じでも、毎回新しいオブジェクトや配列が作られていれば参照が異なるため、再レンダリングが発生してしまいます。
const Component = React.memo(({ obj }) => {
console.log("Component rendered");
return <div>{obj.value}</div>;
});
const App = () => {
const obj = { value: 1 };
return <Component obj={obj} />;
};
この場合、毎回App
がレンダリングされるとobj
は新しいオブジェクトとして生成されるので、オブジェクトの参照が毎回異なり、React.memo
でも再レンダリングが発生してしまいます。
配列やオブジェクトが原因で再レンダリングが発生する例
const Component = React.memo(({ arr }) => {
console.log("Component rendered");
return <div>{arr.join(", ")}</div>;
});
const App = () => {
const arr = [1, 2, 3]; // 新しい配列が毎回生成される
return <Component arr={arr} />;
};
この例では、arr
は毎回新しい配列が生成されるため、浅い比較では参照が異なっていると判断され、毎回再レンダリングが発生します。
浅い比較を最適化する方法
-
useCallback
やuseMemo
の使用
配列やオブジェクトの再生成を防ぐために、useCallback
やuseMemo
を使って同じ参照を保持することができます。
const App = () => {
const arr = useMemo(() => [1, 2, 3], []); // 配列をメモ化
return <Component arr={arr} />;
};
これにより、arr
は最初に生成された後、同じ参照を持ち続けるため、不要な再レンダリングを防ぐことができます。
まとめ
-
浅い比較
プリミティブ型は値を比較し、オブジェクトや配列は参照が同じかどうかを比較します。 -
プリミティブ型の
props
ならReact.memo
で効果的に再レンダリングを抑制できますが、配列やオブジェクト型の場合、毎回新しいものが生成されると再レンダリングが発生します。 -
オブジェクトや配列を再生成しない工夫(
useMemo
やuseCallback
を使うなど)が、パフォーマンス向上に役立ちます。
個人的感想
shallowとdeepはどっかで知る機会があったので、なんとなくプリミティブ型と配列やオブジェクトなどのメモリ参照値なんだろうなと思っていましたが、案の定でした。
配列やオブジェクト型の抑制として、再生成しない工夫が必要とのことで、ここでようやくuseMemo
やuseCallback
が効いてくるんですね。
よくよく考えてみれば、useMemoみたいに計算を端折るパターンはこういったところで特に刺さりそうですね。
useCallbackは正直イマイチまだ認識が甘いので、何とも言えないです。
ここはまた追って学習しなければならないポイントになるかと思います。
にしても部分的なレンダリングやレンダリングの最適化が柔軟に行える反面、適切なパフォーマンス調整が果たして人間の範疇でどこまでできるのやらという気持ちはあります。
ここら辺、コツやツボがあるよ!って分かる方いらっしゃいましたら是非教えて頂けると幸いです。
Discussion