Merge pull request #6046 from gaearon/new-perf

Add new ReactPerf
(cherry picked from commit 98a8f49068)
This commit is contained in:
Dan Abramov
2016-04-29 15:35:53 +01:00
committed by Paul O’Shannessy
parent f50d542ff7
commit 78aa706491
12 changed files with 792 additions and 39 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ var addons = {
docs: 'two-way-binding-helpers',
},
Perf: {
module: 'ReactDefaultPerf',
module: 'ReactPerfAnalysis',
name: 'perf',
docs: 'perf',
},
+1 -1
View File
@@ -34,7 +34,7 @@ React.addons = {
};
if (__DEV__) {
React.addons.Perf = require('ReactDefaultPerf');
React.addons.Perf = require('ReactPerfAnalysis');
React.addons.TestUtils = require('ReactTestUtils');
}
+154 -2
View File
@@ -11,7 +11,9 @@
'use strict';
var ReactInvalidSetStateWarningDevTool = require('ReactInvalidSetStateWarningDevTool');
var ExecutionEnvironment = require('ExecutionEnvironment');
var performanceNow = require('performanceNow');
var warning = require('warning');
var eventHandlers = [];
@@ -37,6 +39,56 @@ function emitEvent(handlerFunctionName, arg1, arg2, arg3, arg4, arg5) {
}
}
var isProfiling = false;
var flushHistory = [];
var currentFlushNesting = 0;
var currentFlushMeasurements = null;
var currentFlushStartTime = null;
var currentTimerDebugID = null;
var currentTimerStartTime = null;
var currentTimerType = null;
function resetMeasurements() {
if (__DEV__) {
if (!isProfiling || currentFlushNesting === 0) {
currentFlushStartTime = null;
currentFlushMeasurements = null;
return;
}
var previousStartTime = currentFlushStartTime;
var previousMeasurements = currentFlushMeasurements || [];
var previousOperations = ReactNativeOperationHistoryDevtool.getHistory();
if (previousMeasurements.length || previousOperations.length) {
var registeredIDs = ReactComponentTreeDevtool.getRegisteredIDs();
flushHistory.push({
duration: performanceNow() - previousStartTime,
measurements: previousMeasurements || [],
operations: previousOperations || [],
treeSnapshot: registeredIDs.reduce((tree, id) => {
var ownerID = ReactComponentTreeDevtool.getOwnerID(id);
var parentID = ReactComponentTreeDevtool.getParentID(id);
tree[id] = {
displayName: ReactComponentTreeDevtool.getDisplayName(id),
text: ReactComponentTreeDevtool.getText(id),
childIDs: ReactComponentTreeDevtool.getChildIDs(id),
// Text nodes don't have owners but this is close enough.
ownerID: ownerID || ReactComponentTreeDevtool.getOwnerID(parentID),
parentID,
};
return tree;
}, {}),
});
}
currentFlushStartTime = performanceNow();
currentFlushMeasurements = [];
ReactComponentTreeDevtool.purgeUnmountedComponents();
ReactNativeOperationHistoryDevtool.clearHistory();
}
}
var ReactDebugTool = {
addDevtool(devtool) {
eventHandlers.push(devtool);
@@ -49,6 +101,95 @@ var ReactDebugTool = {
}
}
},
beginProfiling() {
if (__DEV__) {
if (isProfiling) {
return;
}
isProfiling = true;
flushHistory.length = 0;
resetMeasurements();
}
},
endProfiling() {
if (__DEV__) {
if (!isProfiling) {
return;
}
isProfiling = false;
resetMeasurements();
}
},
getFlushHistory() {
if (__DEV__) {
return flushHistory;
}
},
onBeginFlush() {
if (__DEV__) {
currentFlushNesting++;
resetMeasurements();
}
emitEvent('onBeginFlush');
},
onEndFlush() {
if (__DEV__) {
resetMeasurements();
currentFlushNesting--;
}
emitEvent('onEndFlush');
},
onBeginLifeCycleTimer(debugID, timerType) {
emitEvent('onBeginLifeCycleTimer', debugID, timerType);
if (__DEV__) {
if (isProfiling) {
warning(
!currentTimerType,
'There is an internal error in the React performance measurement code. ' +
'Did not expect %s timer to start while %s timer is still in ' +
'progress for %s instance.',
timerType,
currentTimerType || 'no',
(debugID === currentTimerDebugID) ? 'the same' : 'another'
);
currentTimerStartTime = performanceNow();
currentTimerDebugID = debugID;
currentTimerType = timerType;
}
}
},
onEndLifeCycleTimer(debugID, timerType) {
if (__DEV__) {
if (isProfiling) {
warning(
currentTimerType === timerType,
'There is an internal error in the React performance measurement code. ' +
'We did not expect %s timer to stop while %s timer is still in ' +
'progress for %s instance. Please report this as a bug in React.',
timerType,
currentTimerType || 'no',
(debugID === currentTimerDebugID) ? 'the same' : 'another'
);
currentFlushMeasurements.push({
timerType,
instanceID: debugID,
duration: performance.now() - currentTimerStartTime,
});
currentTimerStartTime = null;
currentTimerDebugID = null;
currentTimerType = null;
}
}
emitEvent('onEndLifeCycleTimer', debugID, timerType);
},
onBeginReconcilerTimer(debugID, timerType) {
emitEvent('onBeginReconcilerTimer', debugID, timerType);
},
onEndReconcilerTimer(debugID, timerType) {
emitEvent('onEndReconcilerTimer', debugID, timerType);
},
onBeginProcessingChildContext() {
emitEvent('onBeginProcessingChildContext');
},
@@ -87,6 +228,17 @@ var ReactDebugTool = {
},
};
ReactDebugTool.addDevtool(ReactInvalidSetStateWarningDevTool);
if (__DEV__) {
var ReactInvalidSetStateWarningDevTool = require('ReactInvalidSetStateWarningDevTool');
var ReactNativeOperationHistoryDevtool = require('ReactNativeOperationHistoryDevtool');
var ReactComponentTreeDevtool = require('ReactComponentTreeDevtool');
ReactDebugTool.addDevtool(ReactInvalidSetStateWarningDevTool);
ReactDebugTool.addDevtool(ReactComponentTreeDevtool);
ReactDebugTool.addDevtool(ReactNativeOperationHistoryDevtool);
var url = (ExecutionEnvironment.canUseDOM && window.location.href) || '';
if ((/[?&]react_perf\b/).test(url)) {
ReactDebugTool.beginProfiling();
}
}
module.exports = ReactDebugTool;
+361
View File
@@ -0,0 +1,361 @@
/**
* Copyright 2016-present, 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 ReactPerfAnalysis
*/
'use strict';
var ReactDebugTool = require('ReactDebugTool');
var warning = require('warning');
function roundFloat(val, base = 2) {
var n = Math.pow(10, base);
return Math.floor(val * n) / n;
}
function getFlushHistory() {
return ReactDebugTool.getFlushHistory();
}
function getExclusive(flushHistory = getFlushHistory()) {
var aggregatedStats = {};
var affectedIDs = {};
function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) {
var {displayName} = treeSnapshot[instanceID];
var key = displayName;
var stats = aggregatedStats[key];
if (!stats) {
affectedIDs[key] = {};
stats = aggregatedStats[key] = {
key,
instanceCount: 0,
counts: {},
durations: {},
totalDuration: 0,
};
}
affectedIDs[key][instanceID] = true;
applyUpdate(stats);
}
flushHistory.forEach(flush => {
var {measurements, treeSnapshot} = flush;
measurements.forEach(measurement => {
var {duration, instanceID, timerType} = measurement;
updateAggregatedStats(treeSnapshot, instanceID, stats => {
stats.totalDuration += duration;
if (!stats.durations[timerType]) {
stats.durations[timerType] = 0;
}
stats.durations[timerType] += duration;
if (!stats.counts[timerType]) {
stats.counts[timerType] = 0;
}
stats.counts[timerType]++;
});
});
});
return Object.keys(aggregatedStats)
.map(key => ({
...aggregatedStats[key],
instanceCount: Object.keys(affectedIDs[key]).length,
}))
.sort((a, b) => b.totalDuration - a.totalDuration);
}
function getInclusive(flushHistory = getFlushHistory(), wastedOnly) {
var aggregatedStats = {};
var affectedIDs = {};
function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) {
var {displayName, ownerID} = treeSnapshot[instanceID];
var owner = treeSnapshot[ownerID];
var key = `${owner ? owner.displayName : '(no owner)'} > ${displayName}`;
var stats = aggregatedStats[key];
if (!stats) {
affectedIDs[key] = {};
stats = aggregatedStats[key] = {
key,
instanceCount: 0,
inclusiveRenderDuration: 0,
renderCount: 0,
};
}
affectedIDs[key][instanceID] = true;
applyUpdate(stats);
}
var hasRenderedByID = {};
flushHistory.forEach(flush => {
var {measurements} = flush;
measurements.forEach(measurement => {
var {instanceID, timerType} = measurement;
if (timerType !== 'render') {
return;
}
hasRenderedByID[instanceID] = true;
});
});
flushHistory.forEach(flush => {
var {measurements, treeSnapshot} = flush;
measurements.forEach(measurement => {
var {duration, instanceID, timerType} = measurement;
if (timerType !== 'render') {
return;
}
updateAggregatedStats(treeSnapshot, instanceID, stats => {
stats.renderCount++;
});
var nextParentID = instanceID;
while (nextParentID) {
if (hasRenderedByID[nextParentID]) {
updateAggregatedStats(treeSnapshot, nextParentID, stats => {
stats.inclusiveRenderDuration += duration;
});
}
nextParentID = treeSnapshot[nextParentID].parentID;
}
});
});
return Object.keys(aggregatedStats)
.map(key => ({
...aggregatedStats[key],
instanceCount: Object.keys(affectedIDs[key]).length,
}))
.sort((a, b) => b.inclusiveRenderDuration - a.inclusiveRenderDuration);
}
function getWasted(flushHistory = getFlushHistory()) {
var aggregatedStats = {};
var affectedIDs = {};
function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) {
var {displayName, ownerID} = treeSnapshot[instanceID];
var owner = treeSnapshot[ownerID];
var key = `${owner ? owner.displayName : '(no owner)'} > ${displayName}`;
var stats = aggregatedStats[key];
if (!stats) {
affectedIDs[key] = {};
stats = aggregatedStats[key] = {
key,
instanceCount: 0,
inclusiveRenderDuration: 0,
renderCount: 0,
};
}
affectedIDs[key][instanceID] = true;
applyUpdate(stats);
}
flushHistory.forEach(flush => {
var {measurements, treeSnapshot, operations} = flush;
var dirtyInstanceIDs = {};
operations.forEach(operation => {
var {instanceID} = operation;
var nextParentID = instanceID;
while (nextParentID) {
dirtyInstanceIDs[nextParentID] = true;
nextParentID = treeSnapshot[nextParentID].parentID;
}
});
var renderedCompositeIDs = {};
measurements.forEach(measurement => {
var {instanceID, timerType} = measurement;
if (timerType !== 'render') {
return;
}
renderedCompositeIDs[instanceID] = true;
});
measurements.forEach(measurement => {
var {duration, instanceID, timerType} = measurement;
if (timerType !== 'render') {
return;
}
var { updateCount } = treeSnapshot[instanceID];
if (dirtyInstanceIDs[instanceID] || updateCount === 0) {
return;
}
updateAggregatedStats(treeSnapshot, instanceID, stats => {
stats.renderCount++;
});
var nextParentID = instanceID;
while (nextParentID) {
if (!renderedCompositeIDs[nextParentID]) {
break;
}
updateAggregatedStats(treeSnapshot, nextParentID, stats => {
stats.inclusiveRenderDuration += duration;
});
nextParentID = treeSnapshot[nextParentID].parentID;
}
});
});
return Object.keys(aggregatedStats)
.map(key => ({
...aggregatedStats[key],
instanceCount: Object.keys(affectedIDs[key]).length,
}))
.sort((a, b) => b.inclusiveRenderDuration - a.inclusiveRenderDuration);
}
function getOperations(flushHistory = getFlushHistory()) {
var stats = [];
flushHistory.forEach((flush, flushIndex) => {
var {operations, treeSnapshot} = flush;
operations.forEach(operation => {
var {instanceID, type, payload} = operation;
var {displayName, ownerID} = treeSnapshot[instanceID];
var owner = treeSnapshot[ownerID];
var key = `${(owner ? owner.displayName : '(no owner)')} > ${displayName}`;
stats.push({
flushIndex,
instanceID,
key,
type,
ownerID,
payload,
});
});
});
return stats;
}
function printExclusive(flushHistory) {
var stats = getExclusive(flushHistory);
var table = stats.map(item => {
var {key, instanceCount, totalDuration} = item;
var renderCount = item.counts.render || 0;
var renderDuration = item.durations.render || 0;
return {
'Component': key,
'Total time (ms)': roundFloat(totalDuration),
'Instance count': instanceCount,
'Total render time (ms)': roundFloat(renderDuration),
'Average render time (ms)': renderCount ?
roundFloat(renderDuration / renderCount) :
undefined,
'Render count': renderCount,
'Total lifecycle time (ms)': roundFloat(totalDuration - renderDuration),
};
});
console.table(table);
}
function printInclusive(flushHistory) {
var stats = getInclusive(flushHistory);
var table = stats.map(item => {
var {key, instanceCount, inclusiveRenderDuration, renderCount} = item;
return {
'Owner > Component': key,
'Inclusive render time (ms)': roundFloat(inclusiveRenderDuration),
'Instance count': instanceCount,
'Render count': renderCount,
};
});
console.table(table);
}
function printWasted(flushHistory) {
var stats = getWasted(flushHistory);
var table = stats.map(item => {
var {key, instanceCount, inclusiveRenderDuration, renderCount} = item;
return {
'Owner > Component': key,
'Inclusive wasted time (ms)': roundFloat(inclusiveRenderDuration),
'Instance count': instanceCount,
'Render count': renderCount,
};
});
console.table(table);
}
function printOperations(flushHistory) {
var stats = getOperations(flushHistory);
var table = stats.map(stat => ({
'Owner > Node': stat.key,
'Operation': stat.type,
'Payload': typeof stat.payload === 'object' ?
JSON.stringify(stat.payload) :
stat.payload,
'Flush index': stat.flushIndex,
'Owner Component ID': stat.ownerID,
'DOM Component ID': stat.instanceID,
}));
console.table(table);
}
var warnedAboutPrintDOM = false;
function printDOM(measurements) {
warning(
warnedAboutPrintDOM,
'`ReactPerf.printDOM(...)` is deprecated. Use ' +
'`ReactPerf.printOperations(...)` instead.'
);
warnedAboutPrintDOM = true;
return printOperations(measurements);
}
var warnedAboutGetMeasurementsSummaryMap = false;
function getMeasurementsSummaryMap(measurements) {
warning(
warnedAboutGetMeasurementsSummaryMap,
'`ReactPerf.getMeasurementsSummaryMap(...)` is deprecated. Use ' +
'`ReactPerf.getWasted(...)` instead.'
);
warnedAboutGetMeasurementsSummaryMap = true;
return getWasted(measurements);
}
function start() {
ReactDebugTool.beginProfiling();
}
function stop() {
ReactDebugTool.endProfiling();
}
var ReactPerfAnalysis = {
getFlushHistory,
getExclusive,
getInclusive,
getWasted,
getOperations,
printExclusive,
printInclusive,
printWasted,
printOperations,
start,
stop,
// Deprecated:
printDOM,
getMeasurementsSummaryMap,
};
module.exports = ReactPerfAnalysis;
@@ -13,7 +13,6 @@
describe('ReactComponentTreeDevtool', () => {
var React;
var ReactDebugTool;
var ReactDOM;
var ReactDOMServer;
var ReactInstanceMap;
@@ -23,17 +22,10 @@ describe('ReactComponentTreeDevtool', () => {
jest.resetModuleRegistry();
React = require('React');
ReactDebugTool = require('ReactDebugTool');
ReactDOM = require('ReactDOM');
ReactDOMServer = require('ReactDOMServer');
ReactInstanceMap = require('ReactInstanceMap');
ReactComponentTreeDevtool = require('ReactComponentTreeDevtool');
ReactDebugTool.addDevtool(ReactComponentTreeDevtool);
});
afterEach(() => {
ReactDebugTool.removeDevtool(ReactComponentTreeDevtool);
});
function getRootDisplayNames() {
@@ -13,7 +13,6 @@
describe('ReactNativeOperationHistoryDevtool', () => {
var React;
var ReactDebugTool;
var ReactDOM;
var ReactDOMComponentTree;
var ReactDOMFeatureFlags;
@@ -23,17 +22,10 @@ describe('ReactNativeOperationHistoryDevtool', () => {
jest.resetModuleRegistry();
React = require('React');
ReactDebugTool = require('ReactDebugTool');
ReactDOM = require('ReactDOM');
ReactDOMComponentTree = require('ReactDOMComponentTree');
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
ReactNativeOperationHistoryDevtool = require('ReactNativeOperationHistoryDevtool');
ReactDebugTool.addDevtool(ReactNativeOperationHistoryDevtool);
});
afterEach(() => {
ReactDebugTool.removeDevtool(ReactNativeOperationHistoryDevtool);
});
function assertHistoryMatches(expectedHistory) {
+5
View File
@@ -308,6 +308,10 @@ var ReactMount = {
shouldReuseMarkup,
context
) {
if (__DEV__) {
ReactInstrumentation.debugTool.onBeginFlush();
}
// Various parts of our code (such as ReactCompositeComponent's
// _renderValidatedComponent) assume that calls to render aren't nested;
// verify that that's the case.
@@ -359,6 +363,7 @@ var ReactMount = {
ReactInstrumentation.debugTool.onMountRootComponent(
componentInstance._renderedComponent._debugID
);
ReactInstrumentation.debugTool.onEndFlush();
}
return componentInstance;
@@ -1104,9 +1104,11 @@ ReactDOMComponent.Mixin = {
this._domID = null;
this._wrapperState = null;
if (this._contentDebugID) {
ReactInstrumentation.debugTool.onUnmountComponent(this._contentDebugID);
this._contentDebugID = null;
if (__DEV__) {
if (this._contentDebugID) {
ReactInstrumentation.debugTool.onUnmountComponent(this._contentDebugID);
this._contentDebugID = null;
}
}
},
@@ -145,7 +145,10 @@ Object.assign(ReactDOMTextComponent.prototype, {
);
if (__DEV__) {
ReactInstrumentation.debugTool.onSetText(this._debugID, nextStringText);
ReactInstrumentation.debugTool.onSetText(
this._debugID,
nextStringText
);
}
}
}
@@ -60,6 +60,40 @@ function warnIfInvalidElement(Component, element) {
}
}
function invokeComponentDidMountWithTimer() {
var publicInstance = this._instance;
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
this._debugID,
'componentDidMount'
);
}
publicInstance.componentDidMount();
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onEndLifeCycleTimer(
this._debugID,
'componentDidMount'
);
}
}
function invokeComponentDidUpdateWithTimer(prevProps, prevState, prevContext) {
var publicInstance = this._instance;
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
this._debugID,
'componentDidUpdate'
);
}
publicInstance.componentDidUpdate(prevProps, prevState, prevContext);
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onEndLifeCycleTimer(
this._debugID,
'componentDidUpdate'
);
}
}
function shouldConstruct(Component) {
return Component.prototype && Component.prototype.isReactComponent;
}
@@ -302,7 +336,11 @@ var ReactCompositeComponentMixin = {
}
if (inst.componentDidMount) {
transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
if (__DEV__) {
transaction.getReactMountReady().enqueue(invokeComponentDidMountWithTimer, this);
} else {
transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
}
}
return markup;
@@ -323,11 +361,47 @@ var ReactCompositeComponentMixin = {
_constructComponentWithoutOwner: function(publicProps, publicContext) {
var Component = this._currentElement.type;
var instanceOrElement;
if (shouldConstruct(Component)) {
return new Component(publicProps, publicContext, ReactUpdateQueue);
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
this._debugID,
'ctor'
);
}
}
instanceOrElement = new Component(publicProps, publicContext, ReactUpdateQueue);
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onEndLifeCycleTimer(
this._debugID,
'ctor'
);
}
}
} else {
return Component(publicProps, publicContext, ReactUpdateQueue);
// This can still be an instance in case of factory components
// but we'll count this as time spent rendering as the more common case.
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
this._debugID,
'render'
);
}
}
instanceOrElement = Component(publicProps, publicContext, ReactUpdateQueue);
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
this._debugID,
'render'
);
}
}
}
return instanceOrElement;
},
performInitialMountWithErrorHandling: function(
@@ -363,7 +437,23 @@ var ReactCompositeComponentMixin = {
performInitialMount: function(renderedElement, nativeParent, nativeContainerInfo, transaction, context) {
var inst = this._instance;
if (inst.componentWillMount) {
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
this._debugID,
'componentWillMount'
);
}
}
inst.componentWillMount();
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onEndLifeCycleTimer(
this._debugID,
'componentWillMount'
);
}
}
// When mounting, calls to `setState` by `componentWillMount` will set
// `this._pendingStateQueue` without triggering a re-render.
if (this._pendingStateQueue) {
@@ -421,12 +511,28 @@ var ReactCompositeComponentMixin = {
if (inst.componentWillUnmount && !inst._calledComponentWillUnmount) {
inst._calledComponentWillUnmount = true;
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
this._debugID,
'componentWillUnmount'
);
}
}
if (safely) {
var name = this.getName() + '.componentWillUnmount()';
ReactErrorUtils.invokeGuardedCallback(name, inst.componentWillUnmount.bind(inst));
} else {
inst.componentWillUnmount();
}
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onEndLifeCycleTimer(
this._debugID,
'componentWillUnmount'
);
}
}
}
if (this._renderedComponent) {
@@ -721,15 +827,47 @@ var ReactCompositeComponentMixin = {
// _pendingStateQueue which will ensure that any state updates gets
// immediately reconciled instead of waiting for the next batch.
if (willReceive && inst.componentWillReceiveProps) {
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
this._debugID,
'componentWillReceiveProps'
);
}
}
inst.componentWillReceiveProps(nextProps, nextContext);
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onEndLifeCycleTimer(
this._debugID,
'componentWillReceiveProps'
);
}
}
}
var nextState = this._processPendingState(nextProps, nextContext);
var shouldUpdate = true;
var shouldUpdate =
this._pendingForceUpdate ||
!inst.shouldComponentUpdate ||
inst.shouldComponentUpdate(nextProps, nextState, nextContext);
if (!this._pendingForceUpdate && inst.shouldComponentUpdate) {
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
this._debugID,
'shouldComponentUpdate'
);
}
}
shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onEndLifeCycleTimer(
this._debugID,
'shouldComponentUpdate'
);
}
}
}
if (__DEV__) {
warning(
@@ -824,7 +962,23 @@ var ReactCompositeComponentMixin = {
}
if (inst.componentWillUpdate) {
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
this._debugID,
'componentWillUpdate'
);
}
}
inst.componentWillUpdate(nextProps, nextState, nextContext);
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onEndLifeCycleTimer(
this._debugID,
'componentWillUpdate'
);
}
}
}
this._currentElement = nextElement;
@@ -836,10 +990,17 @@ var ReactCompositeComponentMixin = {
this._updateRenderedComponent(transaction, unmaskedContext);
if (hasComponentDidUpdate) {
transaction.getReactMountReady().enqueue(
inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext),
inst
);
if (__DEV__) {
transaction.getReactMountReady().enqueue(
invokeComponentDidUpdateWithTimer.bind(this, prevProps, prevState, prevContext),
this
);
} else {
transaction.getReactMountReady().enqueue(
inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext),
inst
);
}
}
},
@@ -914,7 +1075,25 @@ var ReactCompositeComponentMixin = {
*/
_renderValidatedComponentWithoutOwnerOrContext: function() {
var inst = this._instance;
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
this._debugID,
'render'
);
}
}
var renderedComponent = inst.render();
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onEndLifeCycleTimer(
this._debugID,
'render'
);
}
}
if (__DEV__) {
// We allow auto-mocks to proceed as if they're returning null.
if (renderedComponent === undefined &&
@@ -948,6 +1127,7 @@ var ReactCompositeComponentMixin = {
'returned undefined, an array or some other invalid object.',
this.getName() || 'ReactCompositeComponent'
);
return renderedComponent;
},
@@ -42,6 +42,14 @@ var ReactReconciler = {
nativeContainerInfo,
context
) {
if (__DEV__) {
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginReconcilerTimer(
internalInstance._debugID,
'mountComponent'
);
}
}
var markup = internalInstance.mountComponent(
transaction,
nativeParent,
@@ -54,7 +62,13 @@ var ReactReconciler = {
}
if (__DEV__) {
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onMountComponent(internalInstance._debugID);
ReactInstrumentation.debugTool.onEndReconcilerTimer(
internalInstance._debugID,
'mountComponent'
);
ReactInstrumentation.debugTool.onMountComponent(
internalInstance._debugID
);
}
}
return markup;
@@ -75,11 +89,25 @@ var ReactReconciler = {
* @internal
*/
unmountComponent: function(internalInstance, safely) {
if (__DEV__) {
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginReconcilerTimer(
internalInstance._debugID,
'unmountComponent'
);
}
}
ReactRef.detachRefs(internalInstance, internalInstance._currentElement);
internalInstance.unmountComponent(safely);
if (__DEV__) {
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onUnmountComponent(internalInstance._debugID);
ReactInstrumentation.debugTool.onEndReconcilerTimer(
internalInstance._debugID,
'unmountComponent'
);
ReactInstrumentation.debugTool.onUnmountComponent(
internalInstance._debugID
);
}
}
},
@@ -114,6 +142,15 @@ var ReactReconciler = {
return;
}
if (__DEV__) {
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginReconcilerTimer(
internalInstance._debugID,
'receiveComponent'
);
}
}
var refsChanged = ReactRef.shouldUpdateRefs(
prevElement,
nextElement
@@ -133,7 +170,13 @@ var ReactReconciler = {
if (__DEV__) {
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onUpdateComponent(internalInstance._debugID);
ReactInstrumentation.debugTool.onEndReconcilerTimer(
internalInstance._debugID,
'receiveComponent'
);
ReactInstrumentation.debugTool.onUpdateComponent(
internalInstance._debugID
);
}
}
},
@@ -149,10 +192,24 @@ var ReactReconciler = {
internalInstance,
transaction
) {
if (__DEV__) {
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginReconcilerTimer(
internalInstance._debugID,
'performUpdateIfNecessary'
);
}
}
internalInstance.performUpdateIfNecessary(transaction);
if (__DEV__) {
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onUpdateComponent(internalInstance._debugID);
ReactInstrumentation.debugTool.onEndReconcilerTimer(
internalInstance._debugID,
'performUpdateIfNecessary'
);
ReactInstrumentation.debugTool.onUpdateComponent(
internalInstance._debugID
);
}
}
},
@@ -14,6 +14,7 @@
var CallbackQueue = require('CallbackQueue');
var PooledClass = require('PooledClass');
var ReactFeatureFlags = require('ReactFeatureFlags');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactPerf = require('ReactPerf');
var ReactReconciler = require('ReactReconciler');
var Transaction = require('Transaction');
@@ -184,6 +185,10 @@ function runBatchedUpdates(transaction) {
}
var flushBatchedUpdates = function() {
if (__DEV__) {
ReactInstrumentation.debugTool.onBeginFlush();
}
// ReactUpdatesFlushTransaction's wrappers will clear the dirtyComponents
// array and perform any updates enqueued by mount-ready handlers (i.e.,
// componentDidUpdate) but we need to check here too in order to catch
@@ -203,6 +208,10 @@ var flushBatchedUpdates = function() {
CallbackQueue.release(queue);
}
}
if (__DEV__) {
ReactInstrumentation.debugTool.onEndFlush();
}
};
flushBatchedUpdates = ReactPerf.measure(
'ReactUpdates',