Log top-level renders with console.time
Behind a flag.
Ben Alpert committed
Nov 30, 2015 at 19:31 UTC
a0f88d29df808137f0d95fde8d1ee5587540b4bb
5 files changed
+121
src/renderers/dom/client/ReactMount.js
+17
@@ -19,6 +19,7 @@ var ReactDOMComponentTree = require('ReactDOMComponentTree');
19
var ReactDOMContainerInfo = require('ReactDOMContainerInfo');
20
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
21
var ReactElement = require('ReactElement');
22
+var ReactFeatureFlags = require('ReactFeatureFlags');
23
var ReactMarkupChecksum = require('ReactMarkupChecksum');
24
var ReactPerf = require('ReactPerf');
25
var ReactReconciler = require('ReactReconciler');
@@ -96,6 +97,17 @@ function mountComponentIntoNode(
97
shouldReuseMarkup,
98
context
99
) {
100
+ var markerName;
101
+ if (ReactFeatureFlags.logTopLevelRenders) {
102
+ var wrappedElement = wrapperInstance._currentElement.props;
103
+ var type = wrappedElement.type;
104
+ markerName = 'React mount: ' + (
105
+ typeof type === 'string' ? type :
106
+ type.displayName || type.name
107
+ );
108
+ console.time(markerName);
109
+ }
110
+
111
var markup = ReactReconciler.mountComponent(
112
wrapperInstance,
113
transaction,
@@ -103,6 +115,11 @@ function mountComponentIntoNode(
115
ReactDOMContainerInfo(wrapperInstance, container),
116
context
117
);
118
+
119
+ if (markerName) {
120
+ console.timeEnd(markerName);
121
+ }
122
+
123
wrapperInstance._renderedComponent._topLevelWrapper = wrapperInstance;
124
ReactMount._mountImageIntoNode(
125
markup,
src/renderers/dom/client/__tests__/ReactMount-test.js
+31
@@ -279,4 +279,35 @@ describe('ReactMount', function() {
279
ReactDOM.unmountComponentAtNode(container);
280
expect(Object.keys(ReactMount._instancesByReactRootID).length).toBe(1);
281
});
282
+
283
+ it('marks top-level mounts', function() {
284
+ var ReactFeatureFlags = require('ReactFeatureFlags');
285
+
286
+ var Foo = React.createClass({
287
+ render: function() {
288
+ return <Bar />;
289
+ },
290
+ });
291
+
292
+ var Bar = React.createClass({
293
+ render: function() {
294
+ return <div />;
295
+ },
296
+ });
297
+
298
+ try {
299
+ ReactFeatureFlags.logTopLevelRenders = true;
300
+ spyOn(console, 'time');
301
+ spyOn(console, 'timeEnd');
302
+
303
+ ReactTestUtils.renderIntoDocument(<Foo />);
304
+
305
+ expect(console.time.argsForCall.length).toBe(1);
306
+ expect(console.time.argsForCall[0][0]).toBe('React mount: Foo');
307
+ expect(console.timeEnd.argsForCall.length).toBe(1);
308
+ expect(console.timeEnd.argsForCall[0][0]).toBe('React mount: Foo');
309
+ } finally {
310
+ ReactFeatureFlags.logTopLevelRenders = false;
311
+ }
312
+ });
313
});
src/renderers/shared/reconciler/ReactUpdates.js
+19
@@ -13,6 +13,7 @@
13
14
var CallbackQueue = require('CallbackQueue');
15
var PooledClass = require('PooledClass');
16
+var ReactFeatureFlags = require('ReactFeatureFlags');
17
var ReactPerf = require('ReactPerf');
18
var ReactReconciler = require('ReactReconciler');
19
var Transaction = require('Transaction');
@@ -149,11 +150,29 @@ function runBatchedUpdates(transaction) {
150
var callbacks = component._pendingCallbacks;
151
component._pendingCallbacks = null;
152
153
+ var markerName;
154
+ if (ReactFeatureFlags.logTopLevelRenders) {
155
+ var namedComponent = component;
156
+ // Duck type TopLevelWrapper. This is probably always true.
157
+ if (
158
+ component._currentElement.props ===
159
+ component._renderedComponent._currentElement
160
+ ) {
161
+ namedComponent = component._renderedComponent;
162
+ }
163
+ markerName = 'React update: ' + namedComponent.getName();
164
+ console.time(markerName);
165
+ }
166
+
167
ReactReconciler.performUpdateIfNecessary(
168
component,
169
transaction.reconcileTransaction
170
);
171
172
+ if (markerName) {
173
+ console.timeEnd(markerName);
174
+ }
175
+
176
if (callbacks) {
177
for (var j = 0; j < callbacks.length; j++) {
178
transaction.callbackQueue.enqueue(
src/renderers/shared/reconciler/__tests__/ReactUpdates-test.js
+33
@@ -904,4 +904,37 @@ describe('ReactUpdates', function() {
904
expect(renderCount).toBe(1);
905
});
906
907
+ it('marks top-level updates', function() {
908
+ var ReactFeatureFlags = require('ReactFeatureFlags');
909
+
910
+ var Foo = React.createClass({
911
+ render: function() {
912
+ return <Bar />;
913
+ },
914
+ });
915
+
916
+ var Bar = React.createClass({
917
+ render: function() {
918
+ return <div />;
919
+ },
920
+ });
921
+
922
+ var container = document.createElement('div');
923
+ ReactDOM.render(<Foo />, container);
924
+
925
+ try {
926
+ ReactFeatureFlags.logTopLevelRenders = true;
927
+ spyOn(console, 'time');
928
+ spyOn(console, 'timeEnd');
929
+
930
+ ReactDOM.render(<Foo />, container);
931
+
932
+ expect(console.time.argsForCall.length).toBe(1);
933
+ expect(console.time.argsForCall[0][0]).toBe('React update: Foo');
934
+ expect(console.timeEnd.argsForCall.length).toBe(1);
935
+ expect(console.timeEnd.argsForCall[0][0]).toBe('React update: Foo');
936
+ } finally {
937
+ ReactFeatureFlags.logTopLevelRenders = false;
938
+ }
939
+ });
940
});
src/shared/utils/ReactFeatureFlags.js
new
+21
@@ -0,0 +1,21 @@
1
+/**
2
+ * Copyright 2013-2015, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ *
9
+ * @providesModule ReactFeatureFlags
10
+ */
11
+
12
+'use strict';
13
+
14
+var ReactFeatureFlags = {
15
+ // When true, call console.time() before and .timeEnd() after each top-level
16
+ // render (both initial renders and updates). Useful when looking at prod-mode
17
+ // timeline profiles in Chrome, for example.
18
+ logTopLevelRenders: false,
19
+};
20
+
21
+module.exports = ReactFeatureFlags;