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;