Reactの初回レンダリングのコストを下げるために行ったこと

エンジニアの石崎です。

今回は、Reactの初回レンダリングのコストを下げる方法についてお話しします。
私が開発しているプロダクトでは、大量のレコードを含む表のレンダリングに時間がかかっていました。
特に初回マウント時に顕著だったため、その対策について共有したいと思います。根本的な解決策というよりも対処療法的な方法になりますが、参考になれば幸いです。

画面内に入った要素のみ詳細をマウントする

大量のレコードやカードを一覧で表示する場合、初回マウント時に全ての要素をマウントしていたため、画面のレンダリングコストが高くなり、初回表示に時間がかかっていました。そこで、画面内に入った要素のみを詳細にマウントするようにすることで、初回マウント時のレンダリングコストを下げることができました。

Web APIであるIntersectionObserverを使うことで、画面内に入った要素を検知できます。検知するためのuseInViewというReactフックを提供しているライブラリがあるので、それを使うと簡単に実装できます。
こちらが参考のライブラリです: react-intersection-observer

そもそもの話になりますが、全ての要素を一度にレンダリングするのではなく、可能であればページネーションを使用して、フロントエンドにもサーバーにも不要な負荷がかからないようにするのが望ましいと思います。

容量の大きい画像を軽量なものに差し替える

画像を表示する場合、外部ファイルとして<img>タグに読み込ませる方法が一般的です。この方法を取っている場合、画像の描画はReactのレンダリングプロセスから分離されるため、画像のレンダリングがReactのパフォーマンスに大きな影響を与えることは少ないと考えられます。しかし、ライブラリの都合上などの理由でSVGファイルをコンポーネントとして使用する場合があります。この場合、レンダリングプロセスに画像の描画が含まれてしまうため、画像のサイズが大きければその分レンダリングに時間がかかってしまいます。

「SVGはベクターデータだから軽量だろう」と誤解し、ファイルサイズに気づかないまま20MB近いSVG画像をアイコンとして大量にレンダリングしていたことがありました。SVG画像にはベクターデータだけでなくピクセルデータを埋め込むこともできるため、実際には2500x2500のPNG画像をレンダリングしていたようなものでした。

対策として、画像を軽量なものに差し替えて、レンダリングコストを下げるようにしました。根本的な解決策としては、画像を外部ファイルとして読み込むのが望ましいのですが、ライブラリからすぐに分離することが難しかったため、このような対処療法的な方法を取りました。

余談

Reactのレンダリングコストを下げる方法としては、React.memouseMemoなどのメモ化があります。今回は初回マウント時のレンダリングコストを下げる方法にフォーカスしてお話しましたが、メモ化も非常に重要なテクニックです。そのメモ化を自動で行ってくれる「React Compiler」が開発されているとのことで、導入されるのが楽しみです。

まとめ

レンダリングの効率についてお話しました。開発用のPCは比較的スペックが高いため、レンダリングに問題がないと感じることが多いですが、ユーザーの環境では必ずしもそうではありません。ユーザーの環境を考慮して、レンダリングコストを下げるよう心がけましょう。