React開発者ツールを使用してReactコンポーネントをデバッグする方法
著者は、 Creative Commons を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。
序章
Reactアプリは拡張と拡張が迅速に行われるように作られているため、微妙なバグがコードに侵入しやすくなります。 React Developer Toolsブラウザー拡張機能は、各コンポーネントの現在の状態をより深く理解できるようにすることで、これらのバグを追跡するのに役立ちます。 React Developer Toolsは、個々のコンポーネントの現在の props 、状態、および context とともに、Reactコンポーネントツリーを探索するためのインターフェースを提供します。 React Developer Toolsを使用すると、どのコンポーネントが再レンダリングされているかを判断したり、個々のコンポーネントのレンダリングにかかる時間を示すグラフを生成したりすることもできます。 この情報を使用して、非効率的なコードを追跡したり、データ量の多いコンポーネントを最適化したりできます。
このチュートリアルは、ReactDeveloperToolsブラウザー拡張機能をインストールすることから始まります。 次に、テキストアナライザーをテストアプリケーションとして構築します。このアナライザーは、テキストのブロックを取得し、単語数、文字数、文字数などの情報を表示します。 最後に、React Developer Toolsを使用して、テキストアナライザーのコンポーネントを探索し、変化する小道具とコンテキストを追跡します。 例ではChromeブラウザを使用しますが、Firefoxのプラグインを使用することもできます。
このチュートリアルを終了すると、React Developer Toolsを使用して、Reactプロジェクトのデバッグと探索を開始できるようになります。
前提条件
- Chrome React Developer Tools拡張機能を使用するには、 GoogleChromeWebブラウザーまたはオープンソースのChromiumWebブラウザーをダウンロードしてインストールする必要があります。 FireFoxWebブラウザ用のReactDeveloperToolsFireFoxプラグインを使用してフォローすることもできます。
- Node.jsを実行する開発環境が必要になります。 このチュートリアルは、Node.jsバージョン10.22.0およびnpmバージョン6.14.6でテストされました。 これをmacOSまたはUbuntu18.04にインストールするには、Node.jsをインストールしてmacOSにローカル開発環境を作成する方法またはのPPAを使用したインストール]セクションの手順に従います。 Ubuntu18.04にNode.jsをインストールする方法。
- Create ReactAppでセットアップされたReact開発環境。 これを設定するには、ステップ1 — Reactクラスコンポーネントの状態を管理する方法のチュートリアルの空のプロジェクトを作成します。これにより、必須ではないボイラープレートが削除されます。 このチュートリアルでは、プロジェクト名として
debug-tutorial
を使用します。 - このチュートリアルでは、
useState
やコンテキストフックなどのReactコンポーネントとフックを使用します。 コンポーネントとフックについては、チュートリアル Reactでカスタムコンポーネントを作成する方法、 Reactコンポーネントのフックで状態を管理する方法、React間で状態を共有する方法で学ぶことができますContextを持つコンポーネント。 - また、JavaScriptとHTMLの基本的な知識も必要です。これは、HTMLシリーズでWebサイトを構築する方法およびJavaScriptでコーディングする方法にあります。 CSSの基本的な知識も役立ちます。これは、 Mozilla DeveloperNetworkで見つけることができます。
ステップ1— ReactDeveloperTools拡張機能のインストール
このステップでは、ChromeにReactDeveloperToolsブラウザ拡張機能をインストールします。 Chrome JavaScriptコンソールの開発者ツールを使用して、前提条件で作成したdebug-tutorial
プロジェクトのコンポーネントツリーを探索します。 この手順ではChromeを使用しますが、FirefoxにアドオンとしてReactDeveloperToolsをインストールする場合の手順はほぼ同じです。
このステップを完了すると、ブラウザーにReact Developer Toolsがインストールされ、コンポーネントを名前で探索およびフィルター処理できるようになります。
React Developer Toolsは、ChromeおよびFirefoxブラウザー用のプラグインです。 拡張機能を追加すると、開発者コンソールにツールが追加されます。 拡張機能をインストールするには、ReactDeveloperToolsのChromeプラグインページにアクセスしてください。
Chromeに追加ボタンをクリックします。 次に、拡張機能の追加ボタンをクリックして、次のことを確認します。
Chromeが拡張機能をインストールし、成功メッセージと新しいアイコンがブラウザの右上隅のアドレスバーの横に表示されます。
アイコンが表示されない場合は、パズルのピースをクリックしてから、ReactDeveloperToolsの画鋲アイコンをクリックしてアイコンを追加できます。
Reactコンポーネントがないページを表示している場合、アイコンは灰色で表示されます。 ただし、Reactコンポーネントを含むページを表示している場合、アイコンは青と緑で表示されます。 アイコンをクリックすると、アプリケーションがReactの製品版を実行していることが示されます。
digitalocean.com にアクセスして、ホームページでReactの製品版が実行されていることを確認してください。
Reactを使用するWebサイトにアクセスしたので、コンソールを開いてReact開発者ツールにアクセスします。 要素を右クリックして検査するか、[表示]>[開発者]>[JavaScriptコンソール]をクリックしてツールバーを開き、コンソールを開きます。
コンソールを開くと、コンポーネントとプロファイラーの2つの新しいタブが表示されます。
コンポーネントタブには、現在のReactコンポーネントツリーが、小道具、状態、またはコンテキストとともに表示されます。 Profiler タブでは、インタラクションを記録し、コンポーネントのレンダリングを測定できます。 ステップ3でプロファイラータブを調べます。
コンポーネントタブをクリックして、現在のコンポーネントツリーを表示します。
これは本番ビルドであるため、コードは minified になり、コンポーネントにはわかりやすい名前が付けられません。
動作中のWebサイトでReactDeveloperToolsを試したので、テストアプリケーションで使用できます。 debug-tutorial
アプリケーションをまだ起動していない場合は、ターミナルウィンドウに移動し、プロジェクトのルートからnpm start
を実行します。
ブラウザを開いてhttp:// localhost:3000にアクセスします。
ReactDeveloperToolsのアイコンが赤と白になっていることに注意してください。 React Developer Toolsアイコンをクリックすると、ページが開発モードになっているという警告が表示されます。 まだサンプルアプリケーションで作業しているので、これは予想されます。
コンソールを開くと、App
コンポーネントの名前がコンポーネントタブに表示されます。
まだ多くの情報はありませんが、次のステップでプロジェクトを構築すると、すべてのコンポーネントがナビゲート可能なツリーを形成していることがわかります。
このステップでは、ReactDeveloperTools拡張機能をChromeに追加しました。 本番ページと開発ページの両方でツールをアクティブ化し、コンポーネントタブでdebug-tutorial
プロジェクトを簡単に調べました。 次のステップでは、ReactDeveloperToolsの機能を試すために使用するテキストアナライザーを構築します。
ステップ2—リアルタイムコンポーネントの小道具とコンテキストを特定する
このステップでは、テキストのブロックを分析するための小さなアプリケーションを作成します。 アプリは、入力フィールドのテキストの単語数、文字数、および文字頻度を決定して報告します。 アプリケーションをビルドするときは、React Developer Toolsを使用して、各コンポーネントの現在の状態と小道具を調べます。 また、React Developer Toolsを使用して、深くネストされたコンポーネントの現在のコンテキストを表示します。 最後に、ツールを使用して、状態の変化に応じて再レンダリングされるコンポーネントを識別します。
このステップを完了すると、React Developer Toolsを使用して、ライブアプリケーションを探索し、コンソールステートメントやデバッガーなしで現在の小道具と状態を観察できるようになります。
まず、大量のテキストを受け取る入力コンポーネントを作成します。
App.js
ファイルを開きます。
nano src/components/App/App.js
コンポーネント内に、div
のクラスでwrapper
、次に作成しますエレメント周囲[1]
:
debug-tutorial / src / components / App / App.js
import React from 'react'; import './App.css'; function App() { return( <div className="wrapper"> <label htmlFor="text"> Add Your Text Here: <br> <textarea id="text" name="text" rows="10" cols="100" > </textarea> </label> </div> ) } export default App;
これがユーザーの入力領域になります。 htmlFor
属性は、 JSX を使用して、label
要素をtext
のid
を持つ要素にリンクします。 また、<textarea>
コンポーネントの10
行と100
列を指定して、大量のテキスト用のスペースを確保します。
ファイルを保存して閉じます。 次に、App.css
を開きます。
nano src/components/App/App.css
内容を次のように置き換えて、アプリケーションにスタイルを追加します。
debug-tutorial / src / components / App.App.css
.wrapper { padding: 20px; } .wrapper button { background: none; border: black solid 1px; cursor: pointer; margin-right: 10px; } .wrapper div { margin: 20px 0; }
ここでは、wrapper
クラスにパディングを追加してから、背景色を削除してマージンを追加することにより、子<button>
要素を単純化します。 最後に、子<div>
要素に小さなマージンを追加します。 これらのスタイルは、テキストに関する情報を表示するために作成するコンポーネントに適用されます。
ファイルを保存します。 これを行うと、ブラウザが更新され、次の入力が表示されます。
App.js
を開きます:
nano src/components/App/App.js
次に、 context を作成して、<textarea>
要素の値を保持します。 useStateHookを使用してデータをキャプチャします。
debug-tutorial / src / components / App / App.js
import React, { createContext, useState } from 'react'; import './App.css'; export const TextContext = createContext(); function App() { const [text, setText] = useState(''); return( <TextContext.Provider value={text}> <div className="wrapper"> <label htmlFor="text"> Add Your Text Here: <br> <textarea id="text" name="text" rows="10" cols="100" onChange={e => setText(e.target.value)} > </textarea> </label> </div> </TextContext.Provider> ) } export default App;
必ずTextContext
をエクスポートしてから、コンポーネント全体をTextContext.Provider
でラップしてください。 onChange
プロップを<textarea>
要素に追加して、データをキャプチャします。
ファイルを保存します。 ブラウザがリロードされます。 React Developer Toolsが開いていることを確認し、App
コンポーネントがContext.Provider
を子コンポーネントとして表示するようになったことを確認してください。
コンポーネントのデフォルトの総称名はContext
ですが、生成されたコンテキストにdisplayName
プロパティを追加することで変更できます。 App.js
内に、displayName
をTextContext
に設定する行を追加します。
debug-tutorial / src / components / App / App.js
import React, { createContext, useState } from 'react'; import './App.css'; export const TextContext = createContext(); TextContext.displayName = 'TextContext'; function App() { ... } export default App;
displayName
を追加する必要はありませんが、コンソールでコンポーネントツリーを分析するときにコンポーネントをナビゲートするのに役立ちます。 サイドバーにuseState
フックの値も表示されます。 入力にテキストを入力すると、ReactDeveloperToolsのApp
コンポーネントのhooksセクションに更新された値が表示されます。
フックの総称名もState
ですが、これはコンテキストほど簡単には更新できません。 useDebugValue フックがありますが、これはカスタムフックでのみ機能し、すべてのカスタムフックに推奨されるわけではありません。
この場合、App
コンポーネントの状態は、TextContext.Provider
へのプロップです。 React DeveloperToolsのTextContext.Provider
をクリックすると、value
にも状態で設定した入力値が反映されていることがわかります。
React Developer Toolsは、リアルタイムのプロップとコンテキスト情報を表示します。コンポーネントを追加すると、その価値は高まります。
次に、TextInformation
というコンポーネントを追加します。 このコンポーネントは、単語数などの特定のデータ分析を備えたコンポーネントのコンテナになります。
まず、ディレクトリを作成します。
mkdir src/components/TextInformation
次に、テキストエディタでTextInformation.js
を開きます。
nano src/components/TextInformation/TextInformation.js
コンポーネント内には、CharacterCount
、WordCount
、およびCharacterMap
の3つの別個のコンポーネントがあります。 これらのコンポーネントはすぐに作成できます。
TextInformation
コンポーネントは、useReducer
フックを使用して、各コンポーネントの表示を切り替えます。 各コンポーネントの表示値を切り替えるreducer
関数と、onClick
アクションで各コンポーネントを切り替えるボタンを作成します。
debug-tutorial / src / components / TextInformation / TextInformation.js
import React, { useReducer } from 'react'; const reducer = (state, action) => { return { ...state, [action]: !state[action] } } export default function TextInformation() { const [tabs, toggleTabs] = useReducer(reducer, { characterCount: true, wordCount: true, characterMap: true }); return( <div> <button onClick={() => toggleTabs('characterCount')}>Character Count</button> <button onClick={() => toggleTabs('wordCount')}>Word Count</button> <button onClick={() => toggleTabs('characterMap')}>Character Map</button> </div> ) }
useReducer
フックは、各キーをブール値にマップするオブジェクトで始まることに注意してください。 レデューサー関数は、スプレッド演算子を使用して以前の値を保持し、action
パラメーターを使用して新しい値を設定します。
ファイルを保存して閉じます。 次に、App.js
を開きます。
nano src/components/App/App.js
新しいコンポーネントを追加します。
debug-tutorial / src / components / App / App.js
import React, { createContext, useState } from 'react'; import './App.css'; import TextInformation from '../TextInformation/TextInformation'; ... function App() { const [text, setText] = useState(''); return( <TextContext.Provider value={text}> <div className="wrapper"> <label htmlFor="text"> Add Your Text Here: <br> <textarea id="text" name="text" rows="10" cols="100" onChange={e => setText(e.target.value)} > </textarea> </label> <TextInformation /> </div> </TextContext.Provider> ) } export default App;
ファイルを保存して閉じます。 これを行うと、ブラウザがリロードされ、更新されたコンポーネントが表示されます。 React DeveloperToolsでTextInformation
をクリックすると、ボタンをクリックするたびに値が更新されます。
コンテナコンポーネントができたので、各情報コンポーネントを作成する必要があります。 各コンポーネントはshow
と呼ばれる小道具を取ります。 show
が偽の場合、コンポーネントはnull
を返します。 コンポーネントはTextContext
を消費し、データを分析して結果を表示します。
まず、CharacterCount
コンポーネントを作成します。
まず、新しいディレクトリを作成します。
mkdir src/components/CharacterCount
次に、テキストエディタでCharacterCount.js
を開きます。
nano src/components/CharacterCount/CharacterCount.js
コンポーネント内で、show
プロップを使用し、show
が偽の場合にnull
を表示する関数を作成します。
debug-tutorial / src / components / CharacterCount / CharacterCount.js
import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import { TextContext } from '../App/App'; export default function CharacterCount({ show }) { const text = useContext(TextContext); if(!show) { return null; } return( <div> Character Count: {text.length} </div> ) } CharacterCount.proTypes = { show: PropTypes.bool.isRequired }
CharacterCount
関数内で、useContext
フックを使用して、TextContext
の値を変数に割り当てます。 次に、length
メソッドを使用して文字数を示す<div>
を返します。 最後に、 PropTypes は、弱いタイピングシステムを追加して、間違ったプロップタイプが渡されないようにするための強制を提供します。
ファイルを保存して閉じます。 TextInformation.js
を開きます:
nano src/components/TextInformation/TextInformation.js
CharacterCount
をインポートし、ボタンの後にコンポーネントを追加して、tabs.characterCount
をshow
小道具として渡します。
debug-tutorial / src / components / TextInformation / TextInformation.js
import React, { useReducer } from 'react'; import CharacterCount from '../CharacterCount/CharacterCount'; const reducer = (state, action) => { return { ...state, [action]: !state[action] } } export default function TextInformation() { const [tabs, toggleTabs] = useReducer(reducer, { characterCount: true, wordCount: true, characterMap: true }); return( <div> <button onClick={() => toggleTabs('characterCount')}>Character Count</button> <button onClick={() => toggleTabs('wordCount')}>Word Count</button> <button onClick={() => toggleTabs('characterMap')}>Character Map</button> <CharacterCount show={tabs.characterCount} /> </div> ) }
ファイルを保存します。 ブラウザがリロードされ、ReactDeveloperToolsにコンポーネントが表示されます。 入力に単語を追加すると、コンテキストが更新されることに注意してください。 コンポーネントを切り替えると、クリックするたびに小道具が更新されます。
プロパティをクリックして値を更新することにより、小道具を手動で追加または変更することもできます。
次に、WordCount
コンポーネントを追加します。
ディレクトリを作成します。
mkdir src/components/WordCount
テキストエディタでファイルを開きます。
nano src/components/WordCount/WordCount.js
CharacterCount
に似たコンポーネントを作成しますが、スペースで split method を使用して、長さを表示する前に単語のarrayを作成します。
debug-tutorial / src / components / WordCount / WordCount.js
import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import { TextContext } from '../App/App'; export default function WordCount({ show }) { const text = useContext(TextContext); if(!show) { return null; } return( <div> Word Count: {text.split(' ').length} </div> ) } WordCount.proTypes = { show: PropTypes.bool.isRequired }
ファイルを保存して閉じます。
最後に、CharacterMap
コンポーネントを作成します。 このコンポーネントは、テキストのブロックで特定の文字が使用される頻度を示します。 次に、パッセージ内の頻度で文字を並べ替え、結果を表示します。
まず、ディレクトリを作成します。
mkdir src/components/CharacterMap
次に、テキストエディタでCharacterMap.js
を開きます。
nano src/components/CharacterMap/CharacterMap.js
TextContext
コンポーネントをインポートして使用し、show
プロップを使用して、前のコンポーネントで行ったように結果を表示します。
debug-tutorial / src / components / CharacterMap / CharacterMap.js
import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import { TextContext } from '../App/App'; export default function CharacterMap({ show }) { const text = useContext(TextContext); if(!show) { return null; } return( <div> Character Map: {text.length} </div> ) } CharacterMap.proTypes = { show: PropTypes.bool.isRequired }
このコンポーネントでは、各文字の頻度マップを作成するために、もう少し複雑な関数が必要になります。 各文字を調べて、繰り返しがあるときはいつでも値をインクリメントする必要があります。 次に、そのデータを取得して、最も頻度の高い文字がリストの一番上になるように並べ替える必要があります。
これを行うには、次の強調表示されたコードを追加します。
debug-tutorial / src / components / CharacterMap / CharacterMap.js
import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import { TextContext } from '../App/App'; function itemize(text){ const letters = text.split('') .filter(l => l !== ' ') .reduce((collection, item) => { const letter = item.toLowerCase(); return { ...collection, [letter]: (collection[letter] || 0) + 1 } }, {}) return Object.entries(letters) .sort((a, b) => b[1] - a[1]); } export default function CharacterMap({ show }) { const text = useContext(TextContext); if(!show) { return null; } return( <div> Character Map: {itemize(text).map(character => ( <div key={character[0]}> {character[0]}: {character[1]} </div> ))} </div> ) } CharacterMap.proTypes = { show: PropTypes.bool.isRequired }
このコードでは、split()
文字列メソッドを使用して、テキストを文字の配列に分割するitemize
という関数を作成します。 次に、文字を追加し、後続の各文字のカウントをインクリメントすることにより、配列をオブジェクトに削減します。 最後に、Object.entriesとsortを使用してオブジェクトをペアの配列に変換し、最もよく使用される文字を一番上に配置します。
関数を作成したら、render
メソッドで関数にテキストを渡し、結果の上にmap
を渡して、文字(配列値[0]
)とカウントを表示します。配列値[1]
-<div>
内。
ファイルを保存して閉じます。 この関数は、次のセクションでReactDeveloperToolsのいくつかのパフォーマンス機能を探索する機会を提供します。
次に、新しいコンポーネントをTextInformation
に追加し、ReactDeveloperToolsで値を確認します。
TextInformation.js
を開きます:
nano src/components/TextInformation/TextInformation.js
新しいコンポーネントをインポートしてレンダリングします。
debug-tutorial / src / components / TextInformation / TextInformation.js
import React, { useReducer } from 'react'; import CharacterCount from '../CharacterCount/CharacterCount'; import CharacterMap from '../CharacterMap/CharacterMap'; import WordCount from '../WordCount/WordCount'; const reducer = (state, action) => { return { ...state, [action]: !state[action] } } export default function TextInformation() { const [tabs, toggleTabs] = useReducer(reducer, { characterCount: true, wordCount: true, characterMap: true }); return( <div> <button onClick={() => toggleTabs('characterCount')}>Character Count</button> <button onClick={() => toggleTabs('wordCount')}>Word Count</button> <button onClick={() => toggleTabs('characterMap')}>Character Map</button> <CharacterCount show={tabs.characterCount} /> <WordCount show={tabs.wordCount} /> <CharacterMap show={tabs.characterMap} /> </div> ) }
ファイルを保存して閉じます。 これを行うと、ブラウザが更新され、データを追加すると、新しいコンポーネントに文字頻度分析が表示されます。
このセクションでは、ReactDeveloperToolsを使用してコンポーネントツリーを探索しました。 また、各コンポーネントのリアルタイムの小道具を確認する方法と、開発者ツールを使用して小道具を手動で変更する方法も学びました。 最後に、入力によるコンポーネント変更のコンテキストを確認しました。
次のセクションでは、React Developer Tools Profiler タブを使用して、レンダリング時間が長いコンポーネントを特定します。
ステップ3—インタラクション全体でのコンポーネントレンダリングの追跡
このステップでは、React Developer Toolsプロファイラーを使用して、サンプルアプリケーションを使用する際のコンポーネントのレンダリングと再レンダリングを追跡します。 flamegraphs 、またはアプリの関連する最適化指標の視覚化をナビゲートし、その情報を使用して非効率的なコンポーネントを特定し、レンダリング時間を短縮し、アプリケーションの速度を上げます。
この手順を完了すると、ユーザーインタラクション中にレンダリングされるコンポーネントを特定する方法と、非効率的なレンダリングを減らすためにコンポーネントを構成する方法がわかります。
コンポーネントが互いにどのように変化するかをすばやく確認する方法は、コンポーネントが再レンダリングされたときに強調表示を有効にすることです。 これにより、コンポーネントがデータの変化にどのように応答するかを視覚的に確認できます。
React Developer Toolsで、設定アイコンをクリックします。 歯車のように見えます:
次に、 General の下で、コンポーネントがレンダリングされたときに更新を強調表示するというオプションを選択します。
変更を加えると、ReactDeveloperToolsは再レンダリングするコンポーネントを強調表示します。 たとえば、入力を変更すると、データがルートレベルのフックに保存され、変更するたびにコンポーネントツリー全体が再レンダリングされるため、すべてのコンポーネントが再レンダリングされます。
ルートコンポーネントの周りの画面の上部を含む、コンポーネントの周りのハイライトに注意してください。
これを、ボタンの1つをクリックしてデータを切り替えたときにコンポーネントが再レンダリングされる方法と比較してください。 ボタンの1つをクリックすると、TextInformation
の下のコンポーネントは再レンダリングされますが、ルートコンポーネントは再レンダリングされません。
再レンダリングを表示すると、コンポーネントがどのように関連しているかがすぐにわかりますが、特定のコンポーネントを分析するための多くのデータは得られません。 より多くの洞察を得るために、プロファイラーツールを見てみましょう。
プロファイラーツールは、各コンポーネントのレンダリングにかかる時間を正確に測定できるように設計されています。 これは、処理が遅い、または処理が激しいコンポーネントを特定するのに役立ちます。
設定を再度開き、コンポーネントがレンダリングされたときに更新を強調表示するのチェックボックスをオフにします。 次に、コンソールのProfilerタブをクリックします。
プロファイラーを使用するには、画面の左側にある青い円をクリックして記録を開始し、終了したらもう一度クリックします。
記録を停止すると、各アイテムのレンダリングにかかった時間など、コンポーネントの変更のグラフが表示されます。
コンポーネントの相対的な効率をよく理解するには、ウィキペディアのクリエイティブコモンズページに貼り付けてください。 このテキストは興味深い結果を出すのに十分な長さですが、アプリケーションをクラッシュさせるほど大きくはありません。
テキストを貼り付けた後、プロファイラーを起動し、入力に小さな変更を加えます。 コンポーネントの再レンダリングが終了したら、プロファイリングを停止します。 アプリケーションが長い再レンダリングを処理しているため、長い休止が発生します。
記録を終了すると、React Developer Toolsは、再レンダリングされたすべてのコンポーネントと、各コンポーネントの再レンダリングにかかった時間を示すフレームグラフを作成します。
この場合、「変更」という単語からキーを押すたびに再レンダリングが行われます。 さらに重要なのは、各レンダリングにかかる時間と、長い遅延が発生した理由を示していることです。 コンポーネントApp
、TextContext.Provider
、およびTextInformation
は、再レンダリングに約.2ミリ秒かかります。 ただし、CharacterMap
コンポーネントは、itemize
関数での複雑なデータ解析のため、キーストロークごとに約1秒で再レンダリングされます。
ディスプレイでは、各黄色のバーは新しいキーストロークです。 各バーをクリックすると、シーケンスを一度に1つずつ再生できます。 レンダリング時間にはわずかなばらつきがありますが、CharacterMap
は一貫して遅いことに注意してください。
設定のプロファイラーセクションの下にあるプロファイリング中に各コンポーネントがレンダリングされた理由を記録するオプションを選択すると、詳細情報を取得できます。
単語数コンポーネントを切り替えてみて、変更にかかる時間に注意してください。 テキストの内容を変更していなくても、アプリケーションはまだ遅れています。
コンポーネントにカーソルを合わせると、コンポーネントが再レンダリングされた理由が含まれていることがわかります。 この場合、コンポーネントが変更された理由は、レンダリングされた親コンポーネントです。 これはCharacterMap
コンポーネントの問題です。 CharacterMap
は、小道具とコンテキストが変更されていない場合でも、親が変更されるたびにコストのかかる計算を実行しています。 つまり、データが前のレンダリングと同じであっても、データを再計算します。
ランク付けされたタブをクリックすると、他のすべてのコンポーネントと比較した場合、CharacterMap
にかかる時間がわかります。
React Developer Toolsは、問題の切り分けに役立ちました。CharacterMap
コンポーネントは、親コンポーネントが変更されるたびに再レンダリングされ、コストのかかる計算を実行します。
この問題を解決する方法は複数ありますが、いずれもメモ化を介した何らかのキャッシュが含まれます。このプロセスでは、すでに計算されたデータが再計算されるのではなく記憶されます。 lodash /memoizeやmemoize-oneなどのライブラリを使用して、itemize
関数の結果をキャッシュするか、組み込みのReactを使用できます。 memo]コンポーネント全体をメモ化する機能。
React memo
を使用する場合、関数は小道具またはコンテキストが変更された場合にのみ再レンダリングされます。 この場合、Reactmemo
を使用します。 一般に、データ自体はより分離されたケースであるため、最初にメモ化する必要がありますが、コンポーネント全体をメモ化する場合、React Developer Toolsにいくつかの興味深い変更があるため、このチュートリアルではそのアプローチを使用します。
CharacterMap.js
を開きます:
nano src/components/CharacterMap/CharacterMap.js
Reactからmemo
をインポートし、関数全体をmemo
関数に渡します。
debug-tutorial / src / components / CharacterMap / CharacterMap.js
import React, { memo, useContext } from 'react'; import PropTypes from 'prop-types'; import { TextContext } from '../App/App'; ... function CharacterMap({ show }) { const text = useContext(TextContext); if(!show) { return null; } return( <div> Character Map: {itemize(text).map(character => ( <div key={character[0]}> {character[0]}: {character[1]} </div> ))} </div> ) } CharacterMap.proTypes = { show: PropTypes.bool.isRequired } export default memo(CharacterMap);
エクスポートする直前にコンポーネントをmemo
に渡すために、export default
行をコードの最後に移動します。 その後、Reactは再レンダリングする前に小道具を比較します。
ファイルを保存して閉じます。 ブラウザがリロードされ、WordCount
を切り替えると、コンポーネントがはるかに高速に更新されます。 今回は、CharacterMap
は再レンダリングされません。 代わりに、React Developer Toolsで、再レンダリングが阻止されたことを示す灰色の長方形が表示されます。
Ranked タブを見ると、CharacterCount
とWordCount
の両方が再レンダリングされていることがわかりますが、理由は異なります。 CharacterCount
はメモ化されていないため、親が変更されたために再レンダリングされました。 小道具が変更されたため、WordCount
が再レンダリングされました。 memo
でラップされていても、再レンダリングされます。
注:メモ化は役立ちますが、この場合のように、明らかなパフォーマンスの問題がある場合にのみ使用してください。 そうしないと、パフォーマンスの問題が発生する可能性があります。Reactは、再レンダリングするたびに小道具をチェックする必要があり、小さなコンポーネントで遅延が発生する可能性があります。
このステップでは、プロファイラーを使用して、再レンダリングとコンポーネントの再レンダリングを識別しました。 また、フレームグラフとランク付けされたグラフを使用して再レンダリングが遅いコンポーネントを特定し、memo
関数を使用して、小道具やコンテキストに変更がない場合に再レンダリングを防止しました。
結論
React Developer Toolsブラウザー拡張機能は、アプリケーション内のコンポーネントを探索するための強力なユーティリティセットを提供します。 これらのツールを使用すると、コンソールステートメントやデバッガーを使用せずに、実際のデータを使用してコンポーネントの状態を調査し、バグを特定できます。 プロファイラーを使用して、コンポーネントが相互にどのように相互作用するかを調べることもできます。これにより、アプリケーション全体でレンダリングが遅いコンポーネントを特定して最適化できます。 これらのツールは開発プロセスの重要な部分であり、静的コードとしてだけでなく、アプリケーションの一部としてコンポーネントを探索する機会を提供します。
JavaScriptのデバッグについて詳しく知りたい場合は、組み込みのデバッガーとChromeDevToolsを使用してNode.jsをデバッグする方法に関する記事を参照してください。 その他のReactチュートリアルについては、 Reactトピックページを確認するか、React.jsシリーズのコーディング方法ページに戻ってください。