From a0f88d29df808137f0d95fde8d1ee5587540b4bb Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Mon, 30 Nov 2015 19:31:31 -0800 Subject: [PATCH] Log top-level renders with console.time Behind a flag. --- src/renderers/dom/client/ReactMount.js | 17 ++++++++++ .../dom/client/__tests__/ReactMount-test.js | 31 +++++++++++++++++ .../shared/reconciler/ReactUpdates.js | 19 +++++++++++ .../reconciler/__tests__/ReactUpdates-test.js | 33 +++++++++++++++++++ src/shared/utils/ReactFeatureFlags.js | 21 ++++++++++++ 5 files changed, 121 insertions(+) create mode 100644 src/shared/utils/ReactFeatureFlags.js diff --git a/src/renderers/dom/client/ReactMount.js b/src/renderers/dom/client/ReactMount.js index e8cbb251a0..06096189f6 100644 --- a/src/renderers/dom/client/ReactMount.js +++ b/src/renderers/dom/client/ReactMount.js @@ -19,6 +19,7 @@ var ReactDOMComponentTree = require('ReactDOMComponentTree'); var ReactDOMContainerInfo = require('ReactDOMContainerInfo'); var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); var ReactElement = require('ReactElement'); +var ReactFeatureFlags = require('ReactFeatureFlags'); var ReactMarkupChecksum = require('ReactMarkupChecksum'); var ReactPerf = require('ReactPerf'); var ReactReconciler = require('ReactReconciler'); @@ -96,6 +97,17 @@ function mountComponentIntoNode( shouldReuseMarkup, context ) { + var markerName; + if (ReactFeatureFlags.logTopLevelRenders) { + var wrappedElement = wrapperInstance._currentElement.props; + var type = wrappedElement.type; + markerName = 'React mount: ' + ( + typeof type === 'string' ? type : + type.displayName || type.name + ); + console.time(markerName); + } + var markup = ReactReconciler.mountComponent( wrapperInstance, transaction, @@ -103,6 +115,11 @@ function mountComponentIntoNode( ReactDOMContainerInfo(wrapperInstance, container), context ); + + if (markerName) { + console.timeEnd(markerName); + } + wrapperInstance._renderedComponent._topLevelWrapper = wrapperInstance; ReactMount._mountImageIntoNode( markup, diff --git a/src/renderers/dom/client/__tests__/ReactMount-test.js b/src/renderers/dom/client/__tests__/ReactMount-test.js index 7c41815040..cd2ae2b75a 100644 --- a/src/renderers/dom/client/__tests__/ReactMount-test.js +++ b/src/renderers/dom/client/__tests__/ReactMount-test.js @@ -279,4 +279,35 @@ describe('ReactMount', function() { ReactDOM.unmountComponentAtNode(container); expect(Object.keys(ReactMount._instancesByReactRootID).length).toBe(1); }); + + it('marks top-level mounts', function() { + var ReactFeatureFlags = require('ReactFeatureFlags'); + + var Foo = React.createClass({ + render: function() { + return ; + }, + }); + + var Bar = React.createClass({ + render: function() { + return
; + }, + }); + + try { + ReactFeatureFlags.logTopLevelRenders = true; + spyOn(console, 'time'); + spyOn(console, 'timeEnd'); + + ReactTestUtils.renderIntoDocument(); + + expect(console.time.argsForCall.length).toBe(1); + expect(console.time.argsForCall[0][0]).toBe('React mount: Foo'); + expect(console.timeEnd.argsForCall.length).toBe(1); + expect(console.timeEnd.argsForCall[0][0]).toBe('React mount: Foo'); + } finally { + ReactFeatureFlags.logTopLevelRenders = false; + } + }); }); diff --git a/src/renderers/shared/reconciler/ReactUpdates.js b/src/renderers/shared/reconciler/ReactUpdates.js index 841213f7c1..16143e0243 100644 --- a/src/renderers/shared/reconciler/ReactUpdates.js +++ b/src/renderers/shared/reconciler/ReactUpdates.js @@ -13,6 +13,7 @@ var CallbackQueue = require('CallbackQueue'); var PooledClass = require('PooledClass'); +var ReactFeatureFlags = require('ReactFeatureFlags'); var ReactPerf = require('ReactPerf'); var ReactReconciler = require('ReactReconciler'); var Transaction = require('Transaction'); @@ -149,11 +150,29 @@ function runBatchedUpdates(transaction) { var callbacks = component._pendingCallbacks; component._pendingCallbacks = null; + var markerName; + if (ReactFeatureFlags.logTopLevelRenders) { + var namedComponent = component; + // Duck type TopLevelWrapper. This is probably always true. + if ( + component._currentElement.props === + component._renderedComponent._currentElement + ) { + namedComponent = component._renderedComponent; + } + markerName = 'React update: ' + namedComponent.getName(); + console.time(markerName); + } + ReactReconciler.performUpdateIfNecessary( component, transaction.reconcileTransaction ); + if (markerName) { + console.timeEnd(markerName); + } + if (callbacks) { for (var j = 0; j < callbacks.length; j++) { transaction.callbackQueue.enqueue( diff --git a/src/renderers/shared/reconciler/__tests__/ReactUpdates-test.js b/src/renderers/shared/reconciler/__tests__/ReactUpdates-test.js index b9b1c86c99..8665899992 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactUpdates-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactUpdates-test.js @@ -904,4 +904,37 @@ describe('ReactUpdates', function() { expect(renderCount).toBe(1); }); + it('marks top-level updates', function() { + var ReactFeatureFlags = require('ReactFeatureFlags'); + + var Foo = React.createClass({ + render: function() { + return ; + }, + }); + + var Bar = React.createClass({ + render: function() { + return
; + }, + }); + + var container = document.createElement('div'); + ReactDOM.render(, container); + + try { + ReactFeatureFlags.logTopLevelRenders = true; + spyOn(console, 'time'); + spyOn(console, 'timeEnd'); + + ReactDOM.render(, container); + + expect(console.time.argsForCall.length).toBe(1); + expect(console.time.argsForCall[0][0]).toBe('React update: Foo'); + expect(console.timeEnd.argsForCall.length).toBe(1); + expect(console.timeEnd.argsForCall[0][0]).toBe('React update: Foo'); + } finally { + ReactFeatureFlags.logTopLevelRenders = false; + } + }); }); diff --git a/src/shared/utils/ReactFeatureFlags.js b/src/shared/utils/ReactFeatureFlags.js new file mode 100644 index 0000000000..590394b504 --- /dev/null +++ b/src/shared/utils/ReactFeatureFlags.js @@ -0,0 +1,21 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactFeatureFlags + */ + +'use strict'; + +var ReactFeatureFlags = { + // When true, call console.time() before and .timeEnd() after each top-level + // render (both initial renders and updates). Useful when looking at prod-mode + // timeline profiles in Chrome, for example. + logTopLevelRenders: false, +}; + +module.exports = ReactFeatureFlags;