先日、Next.js + PixiJS でアプリーケションを作っている際に、React.memoの使い所が言葉では無く、心で理解できた気がするのでメモを残しておきます。
React.memoとは
もしあるコンポーネントが同じ props を与えられたときに同じ結果をレンダーするなら、結果を記憶してパフォーマンスを向上させるためにそれを React.memo でラップすることができます。つまり、React はコンポーネントのレンダーをスキップし、最後のレンダー結果を再利用します。
https://ja.reactjs.org/docs/react-api.html#reactmemo より引用
基本的にReactでは親要素が再レンダリングされるときに、子コンポーネントも再レンダリングされます。
しかし、React.memoを使えば親コンポーネントが再レンダリングされても、子コンポーネントのレンダリングをスキップすることができるのです。
使い道
CanvasをrequestAnimationFrameで出来る限り更新しながら、兄弟要素のレンダリングをスキップしたいときに使えるということに気づきました。
例えば、こんなコンポーネントを作るかどうかは別として、
・赤い箇所 → Canvas(requestAnimationFrameで更新)
・下のカウント → 経過時間を秒で表示
というものを作る際、React.memoを使わずに書くと、
こんな感じになるかと思います。
このコードで動くといえば動くんですが、この書き方だと下のカウント(Child)もrequestAnimationFrameで更新されてしまいます。
しかし、実際のところ、Childは1秒に1回更新されれば、というか、countが更新されたタイミングで更新されれば充分です。
そんなときに、React.memoを使えば、countが更新されたタイミングでChildを更新することができるのです。ちょいと書き直してみましょう。
差分としては、
const Child = ({ count }) => { console.log('update'); return <div>{ count }</div> };
を、
const Child = React.memo(({ count }) => { console.log('update'); return <div>{ count }</div> });
にしただけ、つまりなんと、ChildをReact.memo()でラップしただけです。非常にお手軽です。
しかし、consoleを見ると、Childのレンダリング回数が減っていることが確認できます。
今回は、ものすごくざっくりした例ですが、メモ化するコンポーネントがもうちょっと重い処理を持っている場合、目に見えてパフォーマンスが改善します。