mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Merge pull request #6046 from gaearon/new-perf
Add new ReactPerf
(cherry picked from commit 98a8f49068)
This commit is contained in:
committed by
Paul O’Shannessy
parent
f50d542ff7
commit
78aa706491
@@ -16,7 +16,7 @@ var addons = {
|
||||
docs: 'two-way-binding-helpers',
|
||||
},
|
||||
Perf: {
|
||||
module: 'ReactDefaultPerf',
|
||||
module: 'ReactPerfAnalysis',
|
||||
name: 'perf',
|
||||
docs: 'perf',
|
||||
},
|
||||
|
||||
@@ -34,7 +34,7 @@ React.addons = {
|
||||
};
|
||||
|
||||
if (__DEV__) {
|
||||
React.addons.Perf = require('ReactDefaultPerf');
|
||||
React.addons.Perf = require('ReactPerfAnalysis');
|
||||
React.addons.TestUtils = require('ReactTestUtils');
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user