このガイドは、一般的なバグやパフォーマンスの問題を特定し、解決する方法を解説していきます。
クライアントサイドのReactのデバッグを扱いますが、サーバーサイドレンダリングを使用するReactアプリを持っている場合は、Node.jsのデバッグガイドやオンデマンドのワークショップの記事でその方法がわかります。ぜひそちらもご覧ください。
以下のセクションでは、次のことを解説していきます。
- Chrome DevTools、VS Code、およびReact Developer Toolsを使用したReactデバッグの基礎
- 最小限のバグと最大限のパフォーマンスを実現するReactアプリを作成する方法
- 一般的なReactのエラーやパフォーマンス課題を特定する方法、それらの課題を改善する方法
- SentryがどのようにしてReactのバグやパフォーマンス課題を本番環境で記録しているか、その問題が発生した際に自ら察知するための方法
Reactデバッグの基礎
Chrome 開発者ツール を使用した React のデバッグ
すべての主要なブラウザ開発ツールには、JavaScriptデバッガが含まれています。
ブラウザからF12キーを押すことで、Chrome 開発者ツールのコンソールにアクセスできます。開発者ツールの「Sources」パネルが開き、3つのセクションが表示されます。次の通りです。
- ページ
- コードエディタ
- デバッガ
「ページ」セクションでは、ページが要求したすべてのファイルのファイルツリーが表示されます。React コンポーネントのファイルを選択すると、そのコードが「コードエディタ」に表示されます。
「デバッガ」には、JavaScript コードを精査するためのツールが表示されます。
「コードエディタ」で行番号をクリックするとブレークポイントを設置することができます。行番号を右クリックし、ポップアップメニューから適切な項目を選択することで、条件付きブレークポイントやログポイントを追加することができます。
イベントリスナーブレークポイントは、「デバッガ」セクションで追加できます。
イベントリスナーブレークポイントとは、さまざまな状況でアプリをデバッグするためにDevToolsで利用可能な多くのブレークポイントの中の一つです。
ちなみに、Chrome for Developersの「Pause your code with breakpoints」で、さまざまなタイプのブレークポイントについて学ぶことができるので、こちらの記事も読んでいただくと、理解をさらに深めることができます。
さて、コード実行がブレークポイントで一時停止すると、「デバッガ」セクションの「スコープ」タブで、一時停止時点でのローカル変数とグローバル変数の値を確認することができます。
スコープ内にある値は、DevToolsウィンドウの左下にある「コンソール」パネルで参照することができます。コンソールパネルが表示されない場合は、Escキーを押してみることを試してください。
また、「デバッガ」の上部にあるボタンを使用して、コードを一つずつステップ実行し、変数の値がどのように変化するかを確認することができます。これは問題のデバッグに役立ちます。
「BasicCounter」コンポーネントの「Increment」ボタンをクリックした時の状態を下の画像で示します。
stateValue変数が増加しない場合がありますが、これはインクリメント関数内でcount状態の値に設定されているにもかかわらず、予期しない動作かもしれません。
コードをステップ実行することで、「Increment」ボタンがクリックされたときにstateValue変数がゼロのままである理由がわかります。
stateValueは、コンポーネントが再レンダリングされるたびに「0」に設定されます。count状態が増加するため「Increment」ボタンがクリックされるとコンポーネントが再レンダリングされるという仕組みです。
ブで、Chrome 開発者ツールを使用したJavaScriptのデバッグについてさらに学ぶことができます。
ただ、Reactのデバッグ作業をブラウザの開発者ツールだけで補おうとしても限界があります。
開発者ツールのデバッガを使用してReactコンポーネントの状態値を更新することは可能ですが、推奨されません。
Reactのpropsとstateは不変であり、直接変更すべきではありません。
代わりに、React Developer Toolsを使用して状態値やpropsを変更してください。それでは、次にReact Developer Toolsの使用方法について説明します。
React Developer Tools
「React 開発者ツール」とも訳せますが、以降そのまま「React Developer Tools」と呼ぶことにします。
さて、React Developer Tools を使用すると、React アプリケーションを分析し、バグやパフォーマンスの問題を特定することが可能です。
React コンポーネントを検査し、props や state を編集し、パフォーマンスとレンダリングをプロファイリングすることができます。
React Developer Tools を使用する最も簡単な方法は、ブラウザ拡張機能をインストールすることです。
これは Chrome、Firefox、および Edge で利用可能です。
他のブラウザを使用している場合は、react-devtools npm パッケージをインストールし、READMEに記載の手順に従ってご利用ください。
React Developer Tools のブラウザ拡張機能をインストールすると、ブラウザの開発者ツールに「Components」と「Profiler」という2つの新しいタブが表示されます。
React デバッグのためにプロファイラを使用する際は、コードの本番バージョンをプロファイルすることを確認してください。
そうすることで最小化(minified)されたコードを使用し、開発時のみのコードなしでアプリを正確に評価することができます。開発用アプリは最小化されておらず、コードの問題を見つけるのに役立つ警告を出す React コードが含まれているため、遅くなります。開発コードは、本番ビルドよりも何倍も遅くなることが起きることがあります。
React Developer Tools を使用した React コンポーネントのデバッグ
The React Developer Tools Components panel displays the React component tree.
React Developer Tools の「Components」パネルは、React コンポーネントツリーを表示します。
コンポーネント名をクリックするか、マウスカーソルを重ねると、ページ上でレンダリングされたコンポーネントが強調されて表示されます。
右側のパネルには、選択されたコンポーネントの props と state が表示されます。これらの値を編集して、UIにどのような影響を与えるかを確認できます。また、アプリと対話するとこれらの値も更新されるため、state 変数の値を追跡し、論理エラーのデバッグに役立ちます。
上記の画像で示されているように、右側のパネル右上には、次の4つのボタンが配置されています。
- 選択したコンポーネントがSuspense境界内にある場合、そのコンポーネントを一時停止する。
- 対応するDOM要素を検査する。
- このコンポーネントのデータをコンソールにログ出力する。
- この要素のソースを表示する。
このボタンをクリックすると、ブラウザの開発者ツールの「Sources」パネルが開きます。ここで、ブラウザのデバッガを使用してコンポーネントをデバッグしたり、コード実行を一時停止してブレークポイントを追加したり、アプリと対話するときに状態値がどのように変化するか確認することができます。
コンポーネントビューでは、左側のパネルの右上隅にある「設定」メニューから、デバッグ、コンポーネントの表示、プロファイリングのオプションを選択できます。
- 「一般」タブで、「コンポーネントのレンダリング時に更新をハイライト表示」チェックボックスを選択すると、再レンダリングをより視覚的にし、わかりやすくなります。
- プロファイラータブで、「プロファイリング中に各コンポーネントのレンダリング理由を記録」チェックボックスを選択すると、レンダリングが発生した原因解析に役立ちます。
React Developer Toolsは、常にコンポーネントの名前を推測するわけではありません。React Context Providersや、memoやforwardRefを使用してラップされたコンポーネントについては、表示名プロパティを設定するようにしてください。各コンポーネントが表示名を持つことを保証するために、eslint-plugin-reactの「react/display-name」ESLintルールを使用することができます。詳細はリンク先をご覧ください。
デフォルトで「State」と名付けられたReactフックの状態値の名前を確認するには、状態値の右側にある「Parse hook names」ボタンをクリックします。
React Developer Toolsを使用したReactアプリケーションのプロファイリング方法
React Profilerは、Reactのレンダリングに関する情報を収集します。
この収集された情報は、React Developer ToolsのProfilerタブに表示されます。
これにより、開発者はパフォーマンスとレンダリングの問題を素早く特定することが可能になります。
Profilerタブの左上にある丸い「Start profiling」の録画ボタンをクリックすることで、Reactのデバッグでプロファイラを使用できます。
プロファイリングが進行中のときはボタンが赤く、進行中でないときは青くなります。
プロファイリングを開始したら、アプリケーションを操作し、その後再び録画ボタンをクリックしてプロファイリングを停止し、収集されたパフォーマンスデータを確認します。たったこれだけです!
録画ボタンの隣には、プロファイリングを開始してリロードするボタン、プロファイリングデータをクリアするボタン、プロファイルJSONデータを読み込むボタン、そしてプロファイルデータをJSONファイルに保存するボタンがあります。
収集された各レンダリングのパフォーマンス情報は、Flamegraph、Ranked、Timelineの3つのタブに分けられて表示されます。
こうした情報をしっかり把握することで、より効果的にパフォーマンスの最適化を行うことができるようになります。
そうすれば、あなたが進めているプロジェクトや戦略に直結したReactアプリの改善に役立てられるでしょう。
Flamegraphタブ
Flamegraphタブの左側のパネルの右上にある棒グラフについて解説します。
これは「各コミットのレンダリング時間」を視覚化し表示しています。
コンポーネントが再レンダリングされると、Reactは変更されたDOMノードのプロパティを計算します。変更がある場合、Reactはそれらの変更をDOMに適用します。これがUI更新のコミットフェーズです。
グラフの各棒はコミットを表し、棒の色と高さはコミットのレンダリングにかかった時間を示しています。黄色い棒は長いレンダリング時間を、緑青色の棒は短いレンダリング時間を示します。
あなたが確認したいレンダリングの棒をクリックすると、そのコミットでの各コンポーネントのレンダリング時間が表示されます。各水平棒の幅は、コンポーネントの最後のレンダリングの期間を表しています。
棒の色は現在のコミットのレンダリング時間を示しています。
灰色の棒があるかもしれませんが、それは現在のコミット中にコンポーネントが再レンダリングされなかったことを示しています。
水平のコンポーネント棒をクリックすると、各コンポーネントのレンダリングに関する詳細情報を表示することができます。
Flamegraphタブの情報を使用して、以下の「一般的なReactパフォーマンスの問題」セクションで説明されているような問題によって引き起こされるパフォーマンスのボトルネックを特定することができます。
上のFlamegraphは、親CountコンポーネントがNumberコンポーネントに関数をpropとして渡している状態を示しています。
Numberコンポーネントは、各レンダリングで高コストの計算を行います。
Numberコンポーネントはメモ化されているため、propsが変更されないときは再レンダリングされるべきではありません。
しかし、FlamegraphはCountコンポーネントが再レンダリングするたびにNumberコンポーネントも再レンダリングされていることを示しています。これはパフォーマンスの問題を引き起こしてしまうリスクを孕んでいます。
useCallbackフックを使用して関数propをラップすることで再レンダリングの問題を解決した後のFlamegraphの状態を、下の画像に示します。
Numberコンポーネントは、Countコンポーネントが再レンダリングして以降、再レンダリングをしていません。また、レンダリング時間も大幅に短縮されています。
この問題とその修正についての詳細な説明は、我々のブログ記事「Reactでメモ化を破壊する再レンダリングを修正する」で詳しく知ることができます。こちらもぜひご覧ください。
ページに多くのコンポーネントがある場合、設定メニュー内のプロファイラーパネルで「Hide commits below」チェックボックスを選択し、ミリ秒単位で値を設定することで、特定の閾値以下のレンダリング時間を持つコミットを非表示にすることができます。
Ranked タブ
Flamegraph タブの棒グラフと同様に、Ranked タブの棒グラフはコミットのレンダリング時間を視覚化します。
しかしFlamegraphタブとの違いとしては、水平の棒がレンダリングにかかった時間によって順序付けられていることです。
Timeline タブ
Timeline タブは、プロファイリング中にコミットがいつ発生したかを示しています。
タイムラインビューでは、多くのコンポーネントが表示されてしまいます。
そこで、タイムライン右上にある「コンポーネント名で検索」を利用することをおすすめします。入力した文字列で、コンポーネントごとにフィルタリングでき、一気に見やすくなります。
Reactアプリの本番ビルドでReact Developer Tools Profilerを使用するには、本番用のプロファイリングバンドルが必要です。これは、react-dom依存関係のプロファイリングフォルダーにあります。このバンドルの使用方法については、「react-domの本番モードでのプロファイリングの使用方法」のガイドをご覧ください。
ReactアプリがViteを使用している場合は、vite.config.jsファイルに以下に示すプロパティを追加できます。
esbuildのコンフィギュレーションはコンポーネント名が最小化(minify)されないようにし、本番ビルドでコンポーネント名がごちゃ混ぜになることを防ぎます。
さて、恥知らずなプラグイン Sentryには、一般的なパフォーマンスの問題を確認し、解決するのに役立つフレームグラフを備えたプロファイリング機能を備えています。
一般的なReactパフォーマンス課題
Reactのパフォーマンスに関する一般的な問題には、以下のようなものがあります。
あなたのアプリケーションは、いくつ該当するでしょうか。ぜひ確認してみてください。
- 不要なコードの読み込み
- 過剰な再レンダリング
- コンポーネントのレンダリングが遅くなる原因となる高負荷な処理
- 膨大なデータリストのレンダリング
- データのキャッシュ、ページ分割、遅延ロードが可能な場合の不要なデータ取得
- useEffectでのメモリリーク
パフォーマンス問題の一般的な原因は、不要なコードの読み込みです。
例えば、Reactアプリが、ページロード時に使用する必要のないチャートライブラリなどの大きなライブラリを使用している場合、必要なときだけライブラリをロードすることで、ページロード速度を向上させることができます。
単純なことのようですが、とても重要かつ見落とされがちなことです。
これを実現するには、JavaScriptのダイナミックインポートを使用します。React 16からは、Susppenseを使ってコンポーネントを遅延ロードできるようになりました。Chrome 開発者ツールの「カバレッジ」パネルは、未使用のJavaScriptコードを見つけるのに便利です。
コンポーネントの不要な再レンダリングは、パフォーマンスの問題を引き起こす可能性が高いです。
再レンダリングは状態の変化によって発生します。コンポーネントが再レンダリングすると、その子や孫のコンポーネントも再レンダリングして、アプリケーションのUIがReactの状態と同期するようになります。
したがって、必要以上にツリーの上位に状態を配置しないことが重要です。
同じpropsを与えられたときに常に同じUIをレンダリングする純粋なコンポーネントがある場合、それをmemoを使ってラップすることで、親コンポーネントが再レンダリングするときに再レンダリングから除外することができます。Reactは、propsが変更された場合にのみ、メモ化されたコンポーネントを再レンダリングします。
レンダリングするたびに、オブジェクトや関数など、コンポーネント内部で定義されたすべての要素が再作成されます。useMemo フックを使用すると、再レンダリングの間に高い負荷のかかる計算結果をキャッシュできます。関数定義をキャッシュするには useCallback を使用します。
memo化は多くの場合必要ありません。
コンポーネントが多くの子孫を持つ場合、コンポーネントが計算コストの高い計算を行う場合、あるいは図形を動かすことができるドローイングエディタのようなインタラクティブ性の高いアプリケーションでは、memo化することでパフォーマンスが向上する場合があります。
また、コンテキストプロバイダのvalue属性に渡された大きなオブジェクトをmemo化するのも便利です。
memoを使用する前に、面倒なコンポーネントを変更可能な部分と変更しない部分に分割できるかどうかを確認することをおすすめします。
Reactの再レンダリングは問題ないことが多いです。
アプリのパフォーマンスに問題があると感じたら、React Developer ToolsのProfilerタブを使用して、再レンダリングがパフォーマンスの問題であるかどうかを確認してみましょう。React Developer Toolsについては次のセクションで詳しく説明します。
なお、Reactチームはコードを自動memo化するReact Compilerを作成中です!
コンパイラがリリースされれば、useMemo、useCallback、memoを使う必要がなくなるかもしれません。
また、非常に長いアイテムのリストをレンダリングする場合にも、よくあるパフォーマンスの問題が発生します。
ReactはDOMを効率的に更新するのが得意ですが、追加または更新するDOMノードが数千個もある場合はさすがに遅くなります。
長いリストをレンダリングする際のパフォーマンスを向上させるには、ユーザーがページとインタラクトする際に、どの項目をDOMに追加する必要があるかを計算し、必要なノードだけを追加するようにします。
このプロセスはリストの仮想化と呼ばれ、TanStack Virtual のようなライブラリを使用すると、大きなリストを効率的にレンダリングすることが可能です。
Sentryを使って、Reactのエラーとパフォーマンスを監視する
アプリケーションが成長するにつれて、本番環境でのエラーログとパフォーマンス監視の重要性は増していきます。できるだけ早く発見し、迅速に対応し、顧客を満足させることが求められます。
Sentryでは、小さなプロジェクトに取り組んでいる個人開発者が無料で使えるフェアソースのエラーモニタリングとトレース機能を提供しています。すべての新しいSentryアカウントには、包括的なパフォーマンス監視などの最新機能にアクセスできる14日間の無料トライアルが付いています。サインアップしたらチュートリアルにしたがって進めるだけで使い方をマスターできます。
Sentryには、ReactアプリでSentryを素早くセットアップするために使用できるReact SDKがあります。
React SDKはJavaScript SDKのラッパーで、Reactに関連する機能が追加されています。React SDKを使用するには、@sentry/reactライブラリをインストールし、アプリケーションのできるだけ早い段階でSentryを初期化します。
一度セットアップすると、Sentryは自動的にアプリケーションの未処理エラーをキャプチャし、Sentryプラットフォームのプロジェクトに送信します。処理されたエラーをログに記録するには、Sentry captureExceptionメソッドの引数にエラーオブジェクトを渡します。
カスタムテキストメッセージを記録したい場合は、captureMessageメソッドを使いましょう。
Sentryダッシュボードのissueページでは、同様のエラーがissueにグループ化されます。エラーのスタックトレースや、どのイベントがエラーにつながったかを示すパンくずを見ることができます。
セッションのリプレイを見ることで、ユーザーがUIで行ったエラーの原因をビデオで見ることができます。
また、特定のイベントに対するアラートを設定したり、サイトのパフォーマンスを監視したり、ユーザビリティの問題をチェックしたり、テストカバレッジをチェックしたり、チームの開発者に問題を割り当てたりすることもできます。
Sentry React SDKには、コンポーネントのレンダリングパフォーマンスを監視するコンポーネントトラッキングなど、React特有の機能が豊富にあります。
Sentryはバックエンドエラーのログも記録でき、多くの異なるプログラミング言語をサポートしているので、フロントエンドとバックエンド両方にご活用いただけます。
Next.jsを使用している場合は、Next.jsインストールウィザードを使用してSentry Next.js SDKをインストールしてください。
Remixを使用している場合は、Remixインストールウィザードを使用してSentry Remix SDKをインストールしてください。React Native SDKもあります。
しかし、ここで起こるのが「プライバシーの問題」です。
ユーザーデータを記録し、ユーザーの活動を追跡することは、プライバシーの問題を孕みます。ですがSentryはユーザーのプライバシーに真剣に取り組んでおり、Sentryに送信されるエラーログから除外するデータを制御するために使用できる「データスクラビングツール」を提供しています。
Reactのデバッグとパフォーマンスに関する最終的な考察
信頼性とパフォーマンスの高い React アプリケーションを提供することは、決して容易ではありません。
Chrome 開発者ツールやReact Developer Toolsのような基本的なツールとSentryのリアルタイムエラー監視とトレースを使用することで、問題を早期に発見し、ユーザー体験を迅速に改善できます。
バグを解消するにしても、パフォーマンスを最適化するにしても、ReactのワークフローにSentryを統合することで、問題を先取りし、アプリケーションをより柔軟に対応することができます。ぜひお気軽にご利用ください。
IchizokuはSentryと提携し、日本でSentry製品の導入支援、テクニカルサポート、ベストプラクティスの共有を行なっています。Ichizokuが提供するSentryの日本語サイトについてはこちらをご覧ください。またご導入についての相談はこちらのフォームからお気軽にお問い合わせください。