11import React from 'react'
2+
23import { Metrics } from './Metrics'
3- import { Results } from './Results'
44import { ErrorComponent } from './ErrorComponent'
55import { Measures } from './Measures'
6+ import { Buttons } from './Buttons'
7+ import { Stats } from './Stats'
8+
9+ import { computeTotalTime , getResults } from '../util'
610
711const theme = require ( '../theme' )
812
@@ -45,16 +49,16 @@ export class ReactPerfDevtool extends React.Component {
4549 pendingEvents : 0 , // Pending event count.
4650 rawMeasures : [ ] , // Raw measures output. It is used for rendering the overall results.
4751 loading : false , // To show the loading output while collecting the results.
48- hasError : false // Track errors, occurred when collecting the measures.
52+ hasError : false , // Track errors, occurred when collecting the measures.
53+ showChart : false
4954 }
5055 }
5156
52- reloadInspectedWindow = ( ) => chrome . devtools . inspectedWindow . reload ( )
53-
5457 componentWillMount ( ) {
5558 // When the devtool is launched first, measures may not be available.
5659 // Reload the window again to get the new measures and then display them.
57- // TODO: Remove this hack (possible architecture change)
60+ // Why ? We rely on the observer hook that the user has installed in his/her project.
61+ // This chrome plugin is just way to interpret the results derived from the observer's API
5862 if ( store . length === 0 ) {
5963 this . reloadInspectedWindow ( )
6064 }
@@ -66,18 +70,94 @@ export class ReactPerfDevtool extends React.Component {
6670
6771 // Get the total measures and flush them if the store is empty.
6872 this . timer = setInterval ( ( ) => this . getMeasuresLength ( ) , 2000 )
73+
74+ // Show the chart when we have the measures
75+ this . setState ( { showChart : true } )
6976 }
7077
7178 componentWillUnmount ( ) {
7279 clearInterval ( this . timer )
7380 }
7481
82+ reloadInspectedWindow = ( ) => chrome . devtools . inspectedWindow . reload ( )
83+
84+ chartOptions = ( {
85+ totalTime,
86+ commitChanges,
87+ hostEffects,
88+ lifecycleTime,
89+ totalComponents,
90+ totalEffects,
91+ totalLifecycleMethods
92+ } ) => ( {
93+ type : 'doughnut' ,
94+ data : {
95+ datasets : [
96+ {
97+ data : [ totalTime , commitChanges , hostEffects , lifecycleTime ] ,
98+ backgroundColor : [ '#ff9999' , '#99ffdd' , '#d98cb3' , '#ffff4d' ]
99+ }
100+ ] ,
101+
102+ // These labels appear in the legend and in the tooltips when hovering different arcs
103+ labels : [
104+ `${ totalComponents } components (ms)` ,
105+ 'Committing changes (ms)' ,
106+ `Committing ${ totalEffects } host ${
107+ totalEffects === 1 || totalEffects === 0 ? 'effect' : 'effects'
108+ } (ms)`,
109+ `Calling ${ totalLifecycleMethods } ${
110+ totalLifecycleMethods === 1 || totalLifecycleMethods === 0
111+ ? 'lifecycle hook'
112+ : 'lifecycle hooks'
113+ } (ms)`
114+ ]
115+ } ,
116+ options : {
117+ responsive : false ,
118+ maintainAspectRatio : false
119+ }
120+ } )
121+
122+ getChartData = props => {
123+ const totalEffects = getResults ( props . rawMeasures ) . totalEffects
124+ const hostEffects = getResults ( props . rawMeasures ) . hostEffectsTime
125+ const commitChanges = getResults ( props . rawMeasures ) . commitChangesTime
126+ const totalLifecycleMethods = getResults ( props . rawMeasures )
127+ . totalLifecycleMethods
128+ const lifecycleTime = getResults ( props . rawMeasures ) . lifecycleTime
129+ const totalTime = computeTotalTime (
130+ props . rawMeasures ,
131+ props . totalTime
132+ ) . toFixed ( 2 )
133+
134+ return {
135+ totalEffects,
136+ hostEffects,
137+ commitChanges,
138+ totalLifecycleMethods,
139+ lifecycleTime,
140+ totalTime,
141+ totalComponents : props . totalComponents
142+ }
143+ }
144+
145+ drawChart = props => {
146+ const contex = document . getElementById ( 'myChart' )
147+ const Chart = this . props . Graphics
148+
149+ const statsChart = new Chart (
150+ contex ,
151+ this . chartOptions ( this . getChartData ( props ) )
152+ )
153+ }
154+
155+ setErrorState = ( ) => this . setState ( { hasError : true , loading : false } )
156+
75157 getMeasuresLength = ( ) => {
76158 this . evaluate ( queries [ 'measuresLength' ] , ( count , err ) => {
77- // TODO: Inspect this behaviour (possibly a bug)
78- // We need to check the measures count also because it may happen that a user reloads the page and see no results.
79- if ( err && count === 0 ) {
80- this . setState ( { hasError : true } )
159+ if ( err || count === 0 ) {
160+ this . setErrorState ( )
81161 return
82162 }
83163
@@ -104,26 +184,27 @@ export class ReactPerfDevtool extends React.Component {
104184 }
105185
106186 getMeasures = ( ) => {
107- // Returns the performance entries which are not parsed and not aggregated (required for generating the overall result)
187+ // Returns the performance entries which are not parsed and not aggregated
188+ // These measures are required for creating the chart.
108189 this . getRawMeasures ( )
109190
110191 this . evaluate ( queries [ 'measures' ] , ( measures , err ) => {
111192 if ( err ) {
112- this . setState ( { hasError : true } )
193+ this . setErrorState ( )
113194 return
114195 }
115196
116- // Update the state.
117197 this . updateMeasures ( JSON . parse ( measures ) )
118198 } )
119199 }
120200
121201 getRawMeasures = ( ) => {
122202 this . evaluate ( queries [ 'rawMeasures' ] , ( measures , err ) => {
123203 if ( err ) {
124- this . setState ( { hasError : true } )
204+ this . setErrorState ( )
125205 return
126206 }
207+
127208 this . setState ( {
128209 rawMeasures : JSON . parse ( measures )
129210 } )
@@ -133,17 +214,25 @@ export class ReactPerfDevtool extends React.Component {
133214 updateMeasures = measures => {
134215 store = store . concat ( measures )
135216
217+ this . drawChart ( {
218+ totalTime : store
219+ . reduce ( ( acc , comp ) => acc + comp . totalTimeSpent , 0 )
220+ . toFixed ( 2 ) ,
221+ rawMeasures : this . state . rawMeasures ,
222+ totalComponents : store . length
223+ } )
224+
136225 this . setState ( {
137226 perfData : store ,
138227 totalTime : store
139228 . reduce ( ( acc , comp ) => acc + comp . totalTimeSpent , 0 )
140229 . toFixed ( 2 )
141230 } )
142231
232+ // Clear the shared state, so that new measures can be appended (and they don't override)
143233 this . clearMeasures ( )
144234 }
145235
146- // TODO: This is not an accurate way to clear the shared state (store on the window object).
147236 clearMeasures = ( ) => this . evaluate ( queries [ 'clear' ] )
148237
149238 // Clear the panel content.
@@ -154,7 +243,8 @@ export class ReactPerfDevtool extends React.Component {
154243 perfData : store ,
155244 totalTime : 0 ,
156245 rawMeasures : [ ] ,
157- pendingEvents : 0
246+ pendingEvents : 0 ,
247+ showChart : false
158248 } )
159249
160250 this . clearMeasures ( )
@@ -174,26 +264,6 @@ export class ReactPerfDevtool extends React.Component {
174264 this . reloadInspectedWindow ( )
175265 }
176266
177- showDocLink = ( ) => {
178- if ( this . state . perfData . length > 0 ) {
179- return (
180- < a
181- className = "doc-link"
182- style = { {
183- textDecoration : 'none' ,
184- paddingBottom : '10px' ,
185- color : theme === 'dark' ? 'blue' : null
186- } }
187- target = "_blank"
188- href = "https://github.com/nitin42/react-perf-devtool"
189- >
190- 👉 Check the documentation to learn more about how these stats
191- are calculated and different phases.
192- </ a >
193- )
194- }
195- }
196-
197267 render ( ) {
198268 if ( this . state . loading ) {
199269 return (
@@ -204,7 +274,7 @@ export class ReactPerfDevtool extends React.Component {
204274 < p
205275 className = { theme === 'dark' ? 'dark-loading-text' : 'loading-text' }
206276 >
207- Connecting to React Performance Devtool ...
277+ Collecting React performance measures ...
208278 </ p >
209279 </ div >
210280 )
@@ -213,34 +283,21 @@ export class ReactPerfDevtool extends React.Component {
213283 return (
214284 < div style = { this . panelStyles } >
215285 < div style = { { display : 'inlineBlock' } } >
216- < button
217- className = { theme === 'dark' ? 'dark-btn' : 'btn' }
218- onClick = { this . clear }
219- >
220- Clear
221- </ button >
222- < button
223- className = { theme === 'dark' ? 'dark-btn' : 'btn' }
224- onClick = { this . reload }
225- >
226- Reload
227- </ button >
286+ < Buttons theme = { theme } clear = { this . clear } reload = { this . reload } />
228287 < span style = { { fontWeight : 'bold' , padding : '8px' } } >
229- Pending Events : { this . state . pendingEvents }
288+ Pending events : { this . state . pendingEvents }
230289 </ span >
231290 </ div >
232291 { this . state . hasError ? (
233292 < ErrorComponent />
234293 ) : (
235294 < React . Fragment >
236295 < Metrics measures = { this . state . perfData } />
237- < Results
238- rawMeasures = { this . state . rawMeasures }
296+ < Stats
297+ showChart = { this . state . showChart }
239298 totalTime = { this . state . totalTime }
240- loading = { this . state . loading }
241299 />
242300 < Measures measures = { this . state . perfData } />
243- { this . showDocLink ( ) }
244301 </ React . Fragment >
245302 ) }
246303 </ div >
0 commit comments