目次
フリーランスになってから早2年。前のプロジェクトが解散となり、新しくReact JSのプロジェクトに参画しました。Reactに関してはほぼ初めてでしたが、最近慣れてきたので、ぼちぼちReactに関する記事も書いていこうかと思います。
React.memo
を使ったときにうまく働いていなさそうだったので、調べたことをまとめました。
通常、Reactのコンポーネントは親が再レンダリングされると、子も同じく再レンダリングされます。
再レンダリング前後で、子コンポーネントに同じプロパティ(props)が渡されるとき、当然、子コンポーネントは同じものになるため、再レンダリングするのはただの無駄です。
React.memo
を用いると、コンポーネントが再レンダリング前と同じプロパティ(props)を受け取ったとき、再レンダリングをスキップできます。
その結果、たくさんのデータを扱ったり、頻繁に画面が更新される場合に、画面描画のパフォーマンスが良くなります。
React.memo
の使い方はいたってシンプルで、関数コンポーネントをラップするだけです。
例えば、以下のようなコードにしておけば、親が再レンダリングされてもコンポーネントに渡されるprops.value
が変わらない限り、再レンダリングされません。
import React from 'react';
const MyComponent = React.memo((props) => {
return (<div>{props.value}</div>);
});
export default MyComponent;
以下の例では、親コンポーネントの「Increment Parent」ボタンをクリックすると親は再レンダリングされますが、React.memo
でラップされたChildComponent
は、childCount
が変わらない限り、表示が変わることがないため再レンダリングされません。
import React, { useState } from 'react';
const ChildComponent = React.memo(({ count }) => {
return (<div>Child Count: {count}</div>);
});
const ParentComponent = () => {
const [parentCount, setParentCount] = useState(0);
const [childCount, setChildCount] = useState(0);
return (
<div>
<div>Parent Count: {parentCount}</div>
<button onClick={() => setParentCount(parentCount + 1)}>
Increment Parent
</button>
<ChildComponent count={childCount} />
<button onClick={() => setChildCount(childCount + 1)}>
Increment Child
</button>
</div>
);
};
export default ParentComponent;
このコードでは、親コンポーネントの「Increment Parent」ボタンを押しても、ChildComponent
は再レンダリングされません。子コンポーネントのカウントが変わらない限り、表示が変わらないためです。
これだけ聞くと、全部React.memo
でラップすればいいじゃないか!と思いがちですが、いくつか注意するべき点があります。
プロパティにオブジェクトや配列が渡された場合、中身が同じであっても、新しいものと認識され、再レンダリングされてしまうことがあります。
React.memo
では、第二引数に比較関数を与えることで、正しく判定することができるようになります。
const MyComponent = React.memo(
(props) => {
return <div>{props.values.join(',')}</div>;
},
(prevProps, nextProps) => {
return prevProps.values.join(',') === nextProps.values.join(',');
}
);
上記のコードでは場合、values
が配列ですが、一度、文字列に変換して中身を比較することで、配列の中身が変わらない限り再レンダリングを防いでいます。
当然ですが、再レンダリングすることがないのに、React.memo
を使ってしまうと逆にパフォーマンスが下がってしまいます。
コンポーネントが超シンプルだったり、再レンダリングすることが少ない場合は、逆にメモ化の処理が負担になることもあるので、本当に必要か考えてから使ったほうがいいと思います。
(ただ、memo化の処理がめちゃめちゃ負荷になることは少ないので、めっちゃ大きいプロジェクトじゃない限り、全部memo化してもいいんじゃね?とか思ってます)
memo
は、不要な再レンダリングを防げて便利ですが、注意しないとうまく動かないことがあります。
オブジェクトや配列のプロパティは、うまく判定できないことがありますが、必要に応じてカスタムの比較関数を使うと、柔軟に再レンダリングのタイミングを制御できます。
propsに配列やオブジェクトがある場合、疑ってみてもいいかもしれません。