mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
Put perf integration behind a feature flag (#11455)
* Enable User Timing API integration with a feature flag * Expose a way to toggle user timing flag in www * Update ReactNativeCSFeatureFlags.js * Update ReactFeatureFlags.js
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
|
||||
import typeof * as CSFeatureFlagsType from './ReactNativeCSFeatureFlags';
|
||||
|
||||
@@ -14,12 +16,18 @@ export const enableAsyncSubtreeAPI = true;
|
||||
export const enableAsyncSchedulingByDefaultInReactDOM = false;
|
||||
export const enableReactFragment = false;
|
||||
export const enableCreateRoot = false;
|
||||
export const enableUserTimingAPI = __DEV__;
|
||||
|
||||
// React Native CS uses persistent reconciler.
|
||||
export const enableMutatingReconciler = false;
|
||||
export const enableNoopReconciler = false;
|
||||
export const enablePersistentReconciler = true;
|
||||
|
||||
// Only used in www builds.
|
||||
export function addUserTimingListener() {
|
||||
invariant(false, 'Not implemented.');
|
||||
}
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
type Check<_X, Y: _X, X: Y=_X> = null;
|
||||
|
||||
+3
@@ -13,6 +13,7 @@ import * as ReactInstanceMap from 'shared/ReactInstanceMap';
|
||||
import * as ReactFiberErrorLogger
|
||||
from 'react-reconciler/src/ReactFiberErrorLogger';
|
||||
import ReactErrorUtils from 'shared/ReactErrorUtils';
|
||||
import {addUserTimingListener} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import ReactDOM from './ReactDOM';
|
||||
import * as ReactBrowserEventEmitter from '../events/ReactBrowserEventEmitter';
|
||||
@@ -31,6 +32,8 @@ Object.assign(
|
||||
ReactInstanceMap,
|
||||
// Used by www msite:
|
||||
TapEventPlugin,
|
||||
// Perf experiment
|
||||
addUserTimingListener,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
+384
-362
@@ -9,6 +9,7 @@
|
||||
|
||||
import type {Fiber} from './ReactFiber';
|
||||
|
||||
import {enableUserTimingAPI} from 'shared/ReactFeatureFlags';
|
||||
import getComponentName from 'shared/getComponentName';
|
||||
import {
|
||||
HostRoot,
|
||||
@@ -19,9 +20,6 @@ import {
|
||||
Fragment,
|
||||
} from 'shared/ReactTypeOfWork';
|
||||
|
||||
// Trust the developer to only use this with a __DEV__ check
|
||||
let ReactDebugFiberPerf = (({}: any): typeof ReactDebugFiberPerf);
|
||||
|
||||
type MeasurementPhase =
|
||||
| 'componentWillMount'
|
||||
| 'componentWillUnmount'
|
||||
@@ -32,382 +30,406 @@ type MeasurementPhase =
|
||||
| 'componentDidMount'
|
||||
| 'getChildContext';
|
||||
|
||||
if (__DEV__) {
|
||||
// Prefix measurements so that it's possible to filter them.
|
||||
// Longer prefixes are hard to read in DevTools.
|
||||
const reactEmoji = '\u269B';
|
||||
const warningEmoji = '\u26D4';
|
||||
const supportsUserTiming =
|
||||
typeof performance !== 'undefined' &&
|
||||
typeof performance.mark === 'function' &&
|
||||
typeof performance.clearMarks === 'function' &&
|
||||
typeof performance.measure === 'function' &&
|
||||
typeof performance.clearMeasures === 'function';
|
||||
// Prefix measurements so that it's possible to filter them.
|
||||
// Longer prefixes are hard to read in DevTools.
|
||||
const reactEmoji = '\u269B';
|
||||
const warningEmoji = '\u26D4';
|
||||
const supportsUserTiming =
|
||||
typeof performance !== 'undefined' &&
|
||||
typeof performance.mark === 'function' &&
|
||||
typeof performance.clearMarks === 'function' &&
|
||||
typeof performance.measure === 'function' &&
|
||||
typeof performance.clearMeasures === 'function';
|
||||
|
||||
// Keep track of current fiber so that we know the path to unwind on pause.
|
||||
// TODO: this looks the same as nextUnitOfWork in scheduler. Can we unify them?
|
||||
let currentFiber: Fiber | null = null;
|
||||
// If we're in the middle of user code, which fiber and method is it?
|
||||
// Reusing `currentFiber` would be confusing for this because user code fiber
|
||||
// can change during commit phase too, but we don't need to unwind it (since
|
||||
// lifecycles in the commit phase don't resemble a tree).
|
||||
let currentPhase: MeasurementPhase | null = null;
|
||||
let currentPhaseFiber: Fiber | null = null;
|
||||
// Did lifecycle hook schedule an update? This is often a performance problem,
|
||||
// so we will keep track of it, and include it in the report.
|
||||
// Track commits caused by cascading updates.
|
||||
let isCommitting: boolean = false;
|
||||
let hasScheduledUpdateInCurrentCommit: boolean = false;
|
||||
let hasScheduledUpdateInCurrentPhase: boolean = false;
|
||||
let commitCountInCurrentWorkLoop: number = 0;
|
||||
let effectCountInCurrentCommit: number = 0;
|
||||
// During commits, we only show a measurement once per method name
|
||||
// to avoid stretch the commit phase with measurement overhead.
|
||||
const labelsInCurrentCommit: Set<string> = new Set();
|
||||
// Keep track of current fiber so that we know the path to unwind on pause.
|
||||
// TODO: this looks the same as nextUnitOfWork in scheduler. Can we unify them?
|
||||
let currentFiber: Fiber | null = null;
|
||||
// If we're in the middle of user code, which fiber and method is it?
|
||||
// Reusing `currentFiber` would be confusing for this because user code fiber
|
||||
// can change during commit phase too, but we don't need to unwind it (since
|
||||
// lifecycles in the commit phase don't resemble a tree).
|
||||
let currentPhase: MeasurementPhase | null = null;
|
||||
let currentPhaseFiber: Fiber | null = null;
|
||||
// Did lifecycle hook schedule an update? This is often a performance problem,
|
||||
// so we will keep track of it, and include it in the report.
|
||||
// Track commits caused by cascading updates.
|
||||
let isCommitting: boolean = false;
|
||||
let hasScheduledUpdateInCurrentCommit: boolean = false;
|
||||
let hasScheduledUpdateInCurrentPhase: boolean = false;
|
||||
let commitCountInCurrentWorkLoop: number = 0;
|
||||
let effectCountInCurrentCommit: number = 0;
|
||||
// During commits, we only show a measurement once per method name
|
||||
// to avoid stretch the commit phase with measurement overhead.
|
||||
const labelsInCurrentCommit: Set<string> = new Set();
|
||||
|
||||
const formatMarkName = (markName: string) => {
|
||||
return `${reactEmoji} ${markName}`;
|
||||
};
|
||||
const formatMarkName = (markName: string) => {
|
||||
return `${reactEmoji} ${markName}`;
|
||||
};
|
||||
|
||||
const formatLabel = (label: string, warning: string | null) => {
|
||||
const prefix = warning ? `${warningEmoji} ` : `${reactEmoji} `;
|
||||
const suffix = warning ? ` Warning: ${warning}` : '';
|
||||
return `${prefix}${label}${suffix}`;
|
||||
};
|
||||
const formatLabel = (label: string, warning: string | null) => {
|
||||
const prefix = warning ? `${warningEmoji} ` : `${reactEmoji} `;
|
||||
const suffix = warning ? ` Warning: ${warning}` : '';
|
||||
return `${prefix}${label}${suffix}`;
|
||||
};
|
||||
|
||||
const beginMark = (markName: string) => {
|
||||
performance.mark(formatMarkName(markName));
|
||||
};
|
||||
const beginMark = (markName: string) => {
|
||||
performance.mark(formatMarkName(markName));
|
||||
};
|
||||
|
||||
const clearMark = (markName: string) => {
|
||||
performance.clearMarks(formatMarkName(markName));
|
||||
};
|
||||
const clearMark = (markName: string) => {
|
||||
performance.clearMarks(formatMarkName(markName));
|
||||
};
|
||||
|
||||
const endMark = (label: string, markName: string, warning: string | null) => {
|
||||
const formattedMarkName = formatMarkName(markName);
|
||||
const formattedLabel = formatLabel(label, warning);
|
||||
try {
|
||||
performance.measure(formattedLabel, formattedMarkName);
|
||||
} catch (err) {
|
||||
// If previous mark was missing for some reason, this will throw.
|
||||
// This could only happen if React crashed in an unexpected place earlier.
|
||||
// Don't pile on with more errors.
|
||||
}
|
||||
// Clear marks immediately to avoid growing buffer.
|
||||
performance.clearMarks(formattedMarkName);
|
||||
performance.clearMeasures(formattedLabel);
|
||||
};
|
||||
const endMark = (label: string, markName: string, warning: string | null) => {
|
||||
const formattedMarkName = formatMarkName(markName);
|
||||
const formattedLabel = formatLabel(label, warning);
|
||||
try {
|
||||
performance.measure(formattedLabel, formattedMarkName);
|
||||
} catch (err) {
|
||||
// If previous mark was missing for some reason, this will throw.
|
||||
// This could only happen if React crashed in an unexpected place earlier.
|
||||
// Don't pile on with more errors.
|
||||
}
|
||||
// Clear marks immediately to avoid growing buffer.
|
||||
performance.clearMarks(formattedMarkName);
|
||||
performance.clearMeasures(formattedLabel);
|
||||
};
|
||||
|
||||
const getFiberMarkName = (label: string, debugID: number) => {
|
||||
return `${label} (#${debugID})`;
|
||||
};
|
||||
const getFiberMarkName = (label: string, debugID: number) => {
|
||||
return `${label} (#${debugID})`;
|
||||
};
|
||||
|
||||
const getFiberLabel = (
|
||||
componentName: string,
|
||||
isMounted: boolean,
|
||||
phase: MeasurementPhase | null,
|
||||
) => {
|
||||
if (phase === null) {
|
||||
// These are composite component total time measurements.
|
||||
return `${componentName} [${isMounted ? 'update' : 'mount'}]`;
|
||||
} else {
|
||||
// Composite component methods.
|
||||
return `${componentName}.${phase}`;
|
||||
}
|
||||
};
|
||||
const getFiberLabel = (
|
||||
componentName: string,
|
||||
isMounted: boolean,
|
||||
phase: MeasurementPhase | null,
|
||||
) => {
|
||||
if (phase === null) {
|
||||
// These are composite component total time measurements.
|
||||
return `${componentName} [${isMounted ? 'update' : 'mount'}]`;
|
||||
} else {
|
||||
// Composite component methods.
|
||||
return `${componentName}.${phase}`;
|
||||
}
|
||||
};
|
||||
|
||||
const beginFiberMark = (
|
||||
fiber: Fiber,
|
||||
phase: MeasurementPhase | null,
|
||||
): boolean => {
|
||||
const componentName = getComponentName(fiber) || 'Unknown';
|
||||
const debugID = ((fiber._debugID: any): number);
|
||||
const isMounted = fiber.alternate !== null;
|
||||
const label = getFiberLabel(componentName, isMounted, phase);
|
||||
const beginFiberMark = (
|
||||
fiber: Fiber,
|
||||
phase: MeasurementPhase | null,
|
||||
): boolean => {
|
||||
const componentName = getComponentName(fiber) || 'Unknown';
|
||||
const debugID = ((fiber._debugID: any): number);
|
||||
const isMounted = fiber.alternate !== null;
|
||||
const label = getFiberLabel(componentName, isMounted, phase);
|
||||
|
||||
if (isCommitting && labelsInCurrentCommit.has(label)) {
|
||||
// During the commit phase, we don't show duplicate labels because
|
||||
// there is a fixed overhead for every measurement, and we don't
|
||||
// want to stretch the commit phase beyond necessary.
|
||||
if (isCommitting && labelsInCurrentCommit.has(label)) {
|
||||
// During the commit phase, we don't show duplicate labels because
|
||||
// there is a fixed overhead for every measurement, and we don't
|
||||
// want to stretch the commit phase beyond necessary.
|
||||
return false;
|
||||
}
|
||||
labelsInCurrentCommit.add(label);
|
||||
|
||||
const markName = getFiberMarkName(label, debugID);
|
||||
beginMark(markName);
|
||||
return true;
|
||||
};
|
||||
|
||||
const clearFiberMark = (fiber: Fiber, phase: MeasurementPhase | null) => {
|
||||
const componentName = getComponentName(fiber) || 'Unknown';
|
||||
const debugID = ((fiber._debugID: any): number);
|
||||
const isMounted = fiber.alternate !== null;
|
||||
const label = getFiberLabel(componentName, isMounted, phase);
|
||||
const markName = getFiberMarkName(label, debugID);
|
||||
clearMark(markName);
|
||||
};
|
||||
|
||||
const endFiberMark = (
|
||||
fiber: Fiber,
|
||||
phase: MeasurementPhase | null,
|
||||
warning: string | null,
|
||||
) => {
|
||||
const componentName = getComponentName(fiber) || 'Unknown';
|
||||
const debugID = ((fiber._debugID: any): number);
|
||||
const isMounted = fiber.alternate !== null;
|
||||
const label = getFiberLabel(componentName, isMounted, phase);
|
||||
const markName = getFiberMarkName(label, debugID);
|
||||
endMark(label, markName, warning);
|
||||
};
|
||||
|
||||
const shouldIgnoreFiber = (fiber: Fiber): boolean => {
|
||||
// Host components should be skipped in the timeline.
|
||||
// We could check typeof fiber.type, but does this work with RN?
|
||||
switch (fiber.tag) {
|
||||
case HostRoot:
|
||||
case HostComponent:
|
||||
case HostText:
|
||||
case HostPortal:
|
||||
case ReturnComponent:
|
||||
case Fragment:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
labelsInCurrentCommit.add(label);
|
||||
}
|
||||
};
|
||||
|
||||
const markName = getFiberMarkName(label, debugID);
|
||||
beginMark(markName);
|
||||
return true;
|
||||
};
|
||||
const clearPendingPhaseMeasurement = () => {
|
||||
if (currentPhase !== null && currentPhaseFiber !== null) {
|
||||
clearFiberMark(currentPhaseFiber, currentPhase);
|
||||
}
|
||||
currentPhaseFiber = null;
|
||||
currentPhase = null;
|
||||
hasScheduledUpdateInCurrentPhase = false;
|
||||
};
|
||||
|
||||
const clearFiberMark = (fiber: Fiber, phase: MeasurementPhase | null) => {
|
||||
const componentName = getComponentName(fiber) || 'Unknown';
|
||||
const debugID = ((fiber._debugID: any): number);
|
||||
const isMounted = fiber.alternate !== null;
|
||||
const label = getFiberLabel(componentName, isMounted, phase);
|
||||
const markName = getFiberMarkName(label, debugID);
|
||||
clearMark(markName);
|
||||
};
|
||||
|
||||
const endFiberMark = (
|
||||
fiber: Fiber,
|
||||
phase: MeasurementPhase | null,
|
||||
warning: string | null,
|
||||
) => {
|
||||
const componentName = getComponentName(fiber) || 'Unknown';
|
||||
const debugID = ((fiber._debugID: any): number);
|
||||
const isMounted = fiber.alternate !== null;
|
||||
const label = getFiberLabel(componentName, isMounted, phase);
|
||||
const markName = getFiberMarkName(label, debugID);
|
||||
endMark(label, markName, warning);
|
||||
};
|
||||
|
||||
const shouldIgnoreFiber = (fiber: Fiber): boolean => {
|
||||
// Host components should be skipped in the timeline.
|
||||
// We could check typeof fiber.type, but does this work with RN?
|
||||
switch (fiber.tag) {
|
||||
case HostRoot:
|
||||
case HostComponent:
|
||||
case HostText:
|
||||
case HostPortal:
|
||||
case ReturnComponent:
|
||||
case Fragment:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const clearPendingPhaseMeasurement = () => {
|
||||
if (currentPhase !== null && currentPhaseFiber !== null) {
|
||||
clearFiberMark(currentPhaseFiber, currentPhase);
|
||||
}
|
||||
currentPhaseFiber = null;
|
||||
currentPhase = null;
|
||||
hasScheduledUpdateInCurrentPhase = false;
|
||||
};
|
||||
|
||||
const pauseTimers = () => {
|
||||
// Stops all currently active measurements so that they can be resumed
|
||||
// if we continue in a later deferred loop from the same unit of work.
|
||||
let fiber = currentFiber;
|
||||
while (fiber) {
|
||||
if (fiber._debugIsCurrentlyTiming) {
|
||||
endFiberMark(fiber, null, null);
|
||||
}
|
||||
fiber = fiber.return;
|
||||
}
|
||||
};
|
||||
|
||||
const resumeTimersRecursively = (fiber: Fiber) => {
|
||||
if (fiber.return !== null) {
|
||||
resumeTimersRecursively(fiber.return);
|
||||
}
|
||||
const pauseTimers = () => {
|
||||
// Stops all currently active measurements so that they can be resumed
|
||||
// if we continue in a later deferred loop from the same unit of work.
|
||||
let fiber = currentFiber;
|
||||
while (fiber) {
|
||||
if (fiber._debugIsCurrentlyTiming) {
|
||||
beginFiberMark(fiber, null);
|
||||
}
|
||||
};
|
||||
|
||||
const resumeTimers = () => {
|
||||
// Resumes all measurements that were active during the last deferred loop.
|
||||
if (currentFiber !== null) {
|
||||
resumeTimersRecursively(currentFiber);
|
||||
}
|
||||
};
|
||||
|
||||
ReactDebugFiberPerf = {
|
||||
recordEffect(): void {
|
||||
effectCountInCurrentCommit++;
|
||||
},
|
||||
|
||||
recordScheduleUpdate(): void {
|
||||
if (isCommitting) {
|
||||
hasScheduledUpdateInCurrentCommit = true;
|
||||
}
|
||||
if (
|
||||
currentPhase !== null &&
|
||||
currentPhase !== 'componentWillMount' &&
|
||||
currentPhase !== 'componentWillReceiveProps'
|
||||
) {
|
||||
hasScheduledUpdateInCurrentPhase = true;
|
||||
}
|
||||
},
|
||||
|
||||
startWorkTimer(fiber: Fiber): void {
|
||||
if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
|
||||
return;
|
||||
}
|
||||
// If we pause, this is the fiber to unwind from.
|
||||
currentFiber = fiber;
|
||||
if (!beginFiberMark(fiber, null)) {
|
||||
return;
|
||||
}
|
||||
fiber._debugIsCurrentlyTiming = true;
|
||||
},
|
||||
|
||||
cancelWorkTimer(fiber: Fiber): void {
|
||||
if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
|
||||
return;
|
||||
}
|
||||
// Remember we shouldn't complete measurement for this fiber.
|
||||
// Otherwise flamechart will be deep even for small updates.
|
||||
fiber._debugIsCurrentlyTiming = false;
|
||||
clearFiberMark(fiber, null);
|
||||
},
|
||||
|
||||
stopWorkTimer(fiber: Fiber): void {
|
||||
if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
|
||||
return;
|
||||
}
|
||||
// If we pause, its parent is the fiber to unwind from.
|
||||
currentFiber = fiber.return;
|
||||
if (!fiber._debugIsCurrentlyTiming) {
|
||||
return;
|
||||
}
|
||||
fiber._debugIsCurrentlyTiming = false;
|
||||
endFiberMark(fiber, null, null);
|
||||
},
|
||||
}
|
||||
fiber = fiber.return;
|
||||
}
|
||||
};
|
||||
|
||||
stopFailedWorkTimer(fiber: Fiber): void {
|
||||
if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
|
||||
return;
|
||||
}
|
||||
// If we pause, its parent is the fiber to unwind from.
|
||||
currentFiber = fiber.return;
|
||||
if (!fiber._debugIsCurrentlyTiming) {
|
||||
return;
|
||||
}
|
||||
fiber._debugIsCurrentlyTiming = false;
|
||||
const warning = 'An error was thrown inside this error boundary';
|
||||
endFiberMark(fiber, null, warning);
|
||||
},
|
||||
const resumeTimersRecursively = (fiber: Fiber) => {
|
||||
if (fiber.return !== null) {
|
||||
resumeTimersRecursively(fiber.return);
|
||||
}
|
||||
if (fiber._debugIsCurrentlyTiming) {
|
||||
beginFiberMark(fiber, null);
|
||||
}
|
||||
};
|
||||
|
||||
startPhaseTimer(fiber: Fiber, phase: MeasurementPhase): void {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
clearPendingPhaseMeasurement();
|
||||
if (!beginFiberMark(fiber, phase)) {
|
||||
return;
|
||||
}
|
||||
currentPhaseFiber = fiber;
|
||||
currentPhase = phase;
|
||||
},
|
||||
const resumeTimers = () => {
|
||||
// Resumes all measurements that were active during the last deferred loop.
|
||||
if (currentFiber !== null) {
|
||||
resumeTimersRecursively(currentFiber);
|
||||
}
|
||||
};
|
||||
|
||||
stopPhaseTimer(): void {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
if (currentPhase !== null && currentPhaseFiber !== null) {
|
||||
const warning = hasScheduledUpdateInCurrentPhase
|
||||
? 'Scheduled a cascading update'
|
||||
: null;
|
||||
endFiberMark(currentPhaseFiber, currentPhase, warning);
|
||||
}
|
||||
currentPhase = null;
|
||||
currentPhaseFiber = null;
|
||||
},
|
||||
|
||||
startWorkLoopTimer(): void {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
commitCountInCurrentWorkLoop = 0;
|
||||
// This is top level call.
|
||||
// Any other measurements are performed within.
|
||||
beginMark('(React Tree Reconciliation)');
|
||||
// Resume any measurements that were in progress during the last loop.
|
||||
resumeTimers();
|
||||
},
|
||||
|
||||
stopWorkLoopTimer(): void {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
const warning = commitCountInCurrentWorkLoop > 1
|
||||
? 'There were cascading updates'
|
||||
: null;
|
||||
commitCountInCurrentWorkLoop = 0;
|
||||
// Pause any measurements until the next loop.
|
||||
pauseTimers();
|
||||
endMark(
|
||||
'(React Tree Reconciliation)',
|
||||
'(React Tree Reconciliation)',
|
||||
warning,
|
||||
);
|
||||
},
|
||||
|
||||
startCommitTimer(): void {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
isCommitting = true;
|
||||
hasScheduledUpdateInCurrentCommit = false;
|
||||
labelsInCurrentCommit.clear();
|
||||
beginMark('(Committing Changes)');
|
||||
},
|
||||
|
||||
stopCommitTimer(): void {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
|
||||
let warning = null;
|
||||
if (hasScheduledUpdateInCurrentCommit) {
|
||||
warning = 'Lifecycle hook scheduled a cascading update';
|
||||
} else if (commitCountInCurrentWorkLoop > 0) {
|
||||
warning = 'Caused by a cascading update in earlier commit';
|
||||
}
|
||||
hasScheduledUpdateInCurrentCommit = false;
|
||||
commitCountInCurrentWorkLoop++;
|
||||
isCommitting = false;
|
||||
labelsInCurrentCommit.clear();
|
||||
|
||||
endMark('(Committing Changes)', '(Committing Changes)', warning);
|
||||
},
|
||||
|
||||
startCommitHostEffectsTimer(): void {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
effectCountInCurrentCommit = 0;
|
||||
beginMark('(Committing Host Effects)');
|
||||
},
|
||||
|
||||
stopCommitHostEffectsTimer(): void {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
const count = effectCountInCurrentCommit;
|
||||
effectCountInCurrentCommit = 0;
|
||||
endMark(
|
||||
`(Committing Host Effects: ${count} Total)`,
|
||||
'(Committing Host Effects)',
|
||||
null,
|
||||
);
|
||||
},
|
||||
|
||||
startCommitLifeCyclesTimer(): void {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
effectCountInCurrentCommit = 0;
|
||||
beginMark('(Calling Lifecycle Methods)');
|
||||
},
|
||||
|
||||
stopCommitLifeCyclesTimer(): void {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
const count = effectCountInCurrentCommit;
|
||||
effectCountInCurrentCommit = 0;
|
||||
endMark(
|
||||
`(Calling Lifecycle Methods: ${count} Total)`,
|
||||
'(Calling Lifecycle Methods)',
|
||||
null,
|
||||
);
|
||||
},
|
||||
};
|
||||
export function recordEffect(): void {
|
||||
if (enableUserTimingAPI) {
|
||||
effectCountInCurrentCommit++;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: convert to named exports
|
||||
// if this doesn't inflate the bundle.
|
||||
export default ReactDebugFiberPerf;
|
||||
export function recordScheduleUpdate(): void {
|
||||
if (enableUserTimingAPI) {
|
||||
if (isCommitting) {
|
||||
hasScheduledUpdateInCurrentCommit = true;
|
||||
}
|
||||
if (
|
||||
currentPhase !== null &&
|
||||
currentPhase !== 'componentWillMount' &&
|
||||
currentPhase !== 'componentWillReceiveProps'
|
||||
) {
|
||||
hasScheduledUpdateInCurrentPhase = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function startWorkTimer(fiber: Fiber): void {
|
||||
if (enableUserTimingAPI) {
|
||||
if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
|
||||
return;
|
||||
}
|
||||
// If we pause, this is the fiber to unwind from.
|
||||
currentFiber = fiber;
|
||||
if (!beginFiberMark(fiber, null)) {
|
||||
return;
|
||||
}
|
||||
fiber._debugIsCurrentlyTiming = true;
|
||||
}
|
||||
}
|
||||
|
||||
export function cancelWorkTimer(fiber: Fiber): void {
|
||||
if (enableUserTimingAPI) {
|
||||
if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
|
||||
return;
|
||||
}
|
||||
// Remember we shouldn't complete measurement for this fiber.
|
||||
// Otherwise flamechart will be deep even for small updates.
|
||||
fiber._debugIsCurrentlyTiming = false;
|
||||
clearFiberMark(fiber, null);
|
||||
}
|
||||
}
|
||||
|
||||
export function stopWorkTimer(fiber: Fiber): void {
|
||||
if (enableUserTimingAPI) {
|
||||
if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
|
||||
return;
|
||||
}
|
||||
// If we pause, its parent is the fiber to unwind from.
|
||||
currentFiber = fiber.return;
|
||||
if (!fiber._debugIsCurrentlyTiming) {
|
||||
return;
|
||||
}
|
||||
fiber._debugIsCurrentlyTiming = false;
|
||||
endFiberMark(fiber, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
export function stopFailedWorkTimer(fiber: Fiber): void {
|
||||
if (enableUserTimingAPI) {
|
||||
if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
|
||||
return;
|
||||
}
|
||||
// If we pause, its parent is the fiber to unwind from.
|
||||
currentFiber = fiber.return;
|
||||
if (!fiber._debugIsCurrentlyTiming) {
|
||||
return;
|
||||
}
|
||||
fiber._debugIsCurrentlyTiming = false;
|
||||
const warning = 'An error was thrown inside this error boundary';
|
||||
endFiberMark(fiber, null, warning);
|
||||
}
|
||||
}
|
||||
|
||||
export function startPhaseTimer(fiber: Fiber, phase: MeasurementPhase): void {
|
||||
if (enableUserTimingAPI) {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
clearPendingPhaseMeasurement();
|
||||
if (!beginFiberMark(fiber, phase)) {
|
||||
return;
|
||||
}
|
||||
currentPhaseFiber = fiber;
|
||||
currentPhase = phase;
|
||||
}
|
||||
}
|
||||
|
||||
export function stopPhaseTimer(): void {
|
||||
if (enableUserTimingAPI) {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
if (currentPhase !== null && currentPhaseFiber !== null) {
|
||||
const warning = hasScheduledUpdateInCurrentPhase
|
||||
? 'Scheduled a cascading update'
|
||||
: null;
|
||||
endFiberMark(currentPhaseFiber, currentPhase, warning);
|
||||
}
|
||||
currentPhase = null;
|
||||
currentPhaseFiber = null;
|
||||
}
|
||||
}
|
||||
|
||||
export function startWorkLoopTimer(): void {
|
||||
if (enableUserTimingAPI) {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
commitCountInCurrentWorkLoop = 0;
|
||||
// This is top level call.
|
||||
// Any other measurements are performed within.
|
||||
beginMark('(React Tree Reconciliation)');
|
||||
// Resume any measurements that were in progress during the last loop.
|
||||
resumeTimers();
|
||||
}
|
||||
}
|
||||
|
||||
export function stopWorkLoopTimer(): void {
|
||||
if (enableUserTimingAPI) {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
const warning = commitCountInCurrentWorkLoop > 1
|
||||
? 'There were cascading updates'
|
||||
: null;
|
||||
commitCountInCurrentWorkLoop = 0;
|
||||
// Pause any measurements until the next loop.
|
||||
pauseTimers();
|
||||
endMark(
|
||||
'(React Tree Reconciliation)',
|
||||
'(React Tree Reconciliation)',
|
||||
warning,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function startCommitTimer(): void {
|
||||
if (enableUserTimingAPI) {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
isCommitting = true;
|
||||
hasScheduledUpdateInCurrentCommit = false;
|
||||
labelsInCurrentCommit.clear();
|
||||
beginMark('(Committing Changes)');
|
||||
}
|
||||
}
|
||||
|
||||
export function stopCommitTimer(): void {
|
||||
if (enableUserTimingAPI) {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
|
||||
let warning = null;
|
||||
if (hasScheduledUpdateInCurrentCommit) {
|
||||
warning = 'Lifecycle hook scheduled a cascading update';
|
||||
} else if (commitCountInCurrentWorkLoop > 0) {
|
||||
warning = 'Caused by a cascading update in earlier commit';
|
||||
}
|
||||
hasScheduledUpdateInCurrentCommit = false;
|
||||
commitCountInCurrentWorkLoop++;
|
||||
isCommitting = false;
|
||||
labelsInCurrentCommit.clear();
|
||||
|
||||
endMark('(Committing Changes)', '(Committing Changes)', warning);
|
||||
}
|
||||
}
|
||||
|
||||
export function startCommitHostEffectsTimer(): void {
|
||||
if (enableUserTimingAPI) {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
effectCountInCurrentCommit = 0;
|
||||
beginMark('(Committing Host Effects)');
|
||||
}
|
||||
}
|
||||
|
||||
export function stopCommitHostEffectsTimer(): void {
|
||||
if (enableUserTimingAPI) {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
const count = effectCountInCurrentCommit;
|
||||
effectCountInCurrentCommit = 0;
|
||||
endMark(
|
||||
`(Committing Host Effects: ${count} Total)`,
|
||||
'(Committing Host Effects)',
|
||||
null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function startCommitLifeCyclesTimer(): void {
|
||||
if (enableUserTimingAPI) {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
effectCountInCurrentCommit = 0;
|
||||
beginMark('(Calling Lifecycle Methods)');
|
||||
}
|
||||
}
|
||||
|
||||
export function stopCommitLifeCyclesTimer(): void {
|
||||
if (enableUserTimingAPI) {
|
||||
if (!supportsUserTiming) {
|
||||
return;
|
||||
}
|
||||
const count = effectCountInCurrentCommit;
|
||||
effectCountInCurrentCommit = 0;
|
||||
endMark(
|
||||
`(Calling Lifecycle Methods: ${count} Total)`,
|
||||
'(Calling Lifecycle Methods)',
|
||||
null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-8
@@ -40,7 +40,7 @@ import invariant from 'fbjs/lib/invariant';
|
||||
import getComponentName from 'shared/getComponentName';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import ReactDebugCurrentFiber from './ReactDebugCurrentFiber';
|
||||
import ReactDebugFiberPerf from './ReactDebugFiberPerf';
|
||||
import {cancelWorkTimer} from './ReactDebugFiberPerf';
|
||||
|
||||
import ReactFiberClassComponent from './ReactFiberClassComponent';
|
||||
import {
|
||||
@@ -60,7 +60,6 @@ import {
|
||||
} from './ReactFiberContext';
|
||||
import {NoWork, Never} from './ReactFiberExpirationTime';
|
||||
|
||||
var {cancelWorkTimer} = ReactDebugFiberPerf;
|
||||
if (__DEV__) {
|
||||
var warnedAboutStatelessRefs = {};
|
||||
}
|
||||
@@ -666,9 +665,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
current,
|
||||
workInProgress: Fiber,
|
||||
): Fiber | null {
|
||||
if (__DEV__) {
|
||||
cancelWorkTimer(workInProgress);
|
||||
}
|
||||
cancelWorkTimer(workInProgress);
|
||||
|
||||
// TODO: We should ideally be able to bail out early if the children have no
|
||||
// more work to do. However, since we don't have a separation of this
|
||||
@@ -689,9 +686,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
}
|
||||
|
||||
function bailoutOnLowPriority(current, workInProgress) {
|
||||
if (__DEV__) {
|
||||
cancelWorkTimer(workInProgress);
|
||||
}
|
||||
cancelWorkTimer(workInProgress);
|
||||
|
||||
// TODO: Handle HostComponent tags here as well and call pushHostContext()?
|
||||
// See PR 8590 discussion for context
|
||||
|
||||
+10
-26
@@ -20,7 +20,7 @@ import shallowEqual from 'fbjs/lib/shallowEqual';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
|
||||
import ReactDebugFiberPerf from './ReactDebugFiberPerf';
|
||||
import {startPhaseTimer, stopPhaseTimer} from './ReactDebugFiberPerf';
|
||||
import {AsyncUpdates} from './ReactTypeOfInternalContext';
|
||||
import {
|
||||
cacheContext,
|
||||
@@ -34,7 +34,6 @@ import {
|
||||
} from './ReactFiberUpdateQueue';
|
||||
import {hasContextChanged} from './ReactFiberContext';
|
||||
|
||||
var {startPhaseTimer, stopPhaseTimer} = ReactDebugFiberPerf;
|
||||
const fakeInternalInstance = {};
|
||||
const isArray = Array.isArray;
|
||||
|
||||
@@ -161,17 +160,13 @@ export default function(
|
||||
const instance = workInProgress.stateNode;
|
||||
const type = workInProgress.type;
|
||||
if (typeof instance.shouldComponentUpdate === 'function') {
|
||||
if (__DEV__) {
|
||||
startPhaseTimer(workInProgress, 'shouldComponentUpdate');
|
||||
}
|
||||
startPhaseTimer(workInProgress, 'shouldComponentUpdate');
|
||||
const shouldUpdate = instance.shouldComponentUpdate(
|
||||
newProps,
|
||||
newState,
|
||||
newContext,
|
||||
);
|
||||
if (__DEV__) {
|
||||
stopPhaseTimer();
|
||||
}
|
||||
stopPhaseTimer();
|
||||
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
@@ -365,14 +360,11 @@ export default function(
|
||||
}
|
||||
|
||||
function callComponentWillMount(workInProgress, instance) {
|
||||
if (__DEV__) {
|
||||
startPhaseTimer(workInProgress, 'componentWillMount');
|
||||
}
|
||||
startPhaseTimer(workInProgress, 'componentWillMount');
|
||||
const oldState = instance.state;
|
||||
instance.componentWillMount();
|
||||
if (__DEV__) {
|
||||
stopPhaseTimer();
|
||||
}
|
||||
|
||||
stopPhaseTimer();
|
||||
|
||||
if (oldState !== instance.state) {
|
||||
if (__DEV__) {
|
||||
@@ -394,14 +386,10 @@ export default function(
|
||||
newProps,
|
||||
newContext,
|
||||
) {
|
||||
if (__DEV__) {
|
||||
startPhaseTimer(workInProgress, 'componentWillReceiveProps');
|
||||
}
|
||||
startPhaseTimer(workInProgress, 'componentWillReceiveProps');
|
||||
const oldState = instance.state;
|
||||
instance.componentWillReceiveProps(newProps, newContext);
|
||||
if (__DEV__) {
|
||||
stopPhaseTimer();
|
||||
}
|
||||
stopPhaseTimer();
|
||||
|
||||
if (instance.state !== oldState) {
|
||||
if (__DEV__) {
|
||||
@@ -673,13 +661,9 @@ export default function(
|
||||
|
||||
if (shouldUpdate) {
|
||||
if (typeof instance.componentWillUpdate === 'function') {
|
||||
if (__DEV__) {
|
||||
startPhaseTimer(workInProgress, 'componentWillUpdate');
|
||||
}
|
||||
startPhaseTimer(workInProgress, 'componentWillUpdate');
|
||||
instance.componentWillUpdate(newProps, newState, newContext);
|
||||
if (__DEV__) {
|
||||
stopPhaseTimer();
|
||||
}
|
||||
stopPhaseTimer();
|
||||
}
|
||||
if (typeof instance.componentDidUpdate === 'function') {
|
||||
workInProgress.effectTag |= Update;
|
||||
|
||||
+14
-27
@@ -29,10 +29,9 @@ import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
import {commitCallbacks} from './ReactFiberUpdateQueue';
|
||||
import {onCommitUnmount} from './ReactFiberDevToolsHook';
|
||||
import ReactDebugFiberPerf from './ReactDebugFiberPerf';
|
||||
import {startPhaseTimer, stopPhaseTimer} from './ReactDebugFiberPerf';
|
||||
|
||||
var {invokeGuardedCallback, hasCaughtError, clearCaughtError} = ReactErrorUtils;
|
||||
var {startPhaseTimer, stopPhaseTimer} = ReactDebugFiberPerf;
|
||||
|
||||
export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
config: HostConfig<T, P, I, TI, PI, C, CC, CX, PL>,
|
||||
@@ -40,22 +39,20 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
) {
|
||||
const {getPublicInstance, mutation, persistence} = config;
|
||||
|
||||
if (__DEV__) {
|
||||
var callComponentWillUnmountWithTimerInDev = function(current, instance) {
|
||||
startPhaseTimer(current, 'componentWillUnmount');
|
||||
instance.props = current.memoizedProps;
|
||||
instance.state = current.memoizedState;
|
||||
instance.componentWillUnmount();
|
||||
stopPhaseTimer();
|
||||
};
|
||||
}
|
||||
var callComponentWillUnmountWithTimer = function(current, instance) {
|
||||
startPhaseTimer(current, 'componentWillUnmount');
|
||||
instance.props = current.memoizedProps;
|
||||
instance.state = current.memoizedState;
|
||||
instance.componentWillUnmount();
|
||||
stopPhaseTimer();
|
||||
};
|
||||
|
||||
// Capture errors so they don't interrupt unmounting.
|
||||
function safelyCallComponentWillUnmount(current, instance) {
|
||||
if (__DEV__) {
|
||||
invokeGuardedCallback(
|
||||
null,
|
||||
callComponentWillUnmountWithTimerInDev,
|
||||
callComponentWillUnmountWithTimer,
|
||||
null,
|
||||
current,
|
||||
instance,
|
||||
@@ -66,9 +63,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
instance.props = current.memoizedProps;
|
||||
instance.state = current.memoizedState;
|
||||
instance.componentWillUnmount();
|
||||
callComponentWillUnmountWithTimer(current, instance);
|
||||
} catch (unmountError) {
|
||||
captureError(current, unmountError);
|
||||
}
|
||||
@@ -100,27 +95,19 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
const instance = finishedWork.stateNode;
|
||||
if (finishedWork.effectTag & Update) {
|
||||
if (current === null) {
|
||||
if (__DEV__) {
|
||||
startPhaseTimer(finishedWork, 'componentDidMount');
|
||||
}
|
||||
startPhaseTimer(finishedWork, 'componentDidMount');
|
||||
instance.props = finishedWork.memoizedProps;
|
||||
instance.state = finishedWork.memoizedState;
|
||||
instance.componentDidMount();
|
||||
if (__DEV__) {
|
||||
stopPhaseTimer();
|
||||
}
|
||||
stopPhaseTimer();
|
||||
} else {
|
||||
const prevProps = current.memoizedProps;
|
||||
const prevState = current.memoizedState;
|
||||
if (__DEV__) {
|
||||
startPhaseTimer(finishedWork, 'componentDidUpdate');
|
||||
}
|
||||
startPhaseTimer(finishedWork, 'componentDidUpdate');
|
||||
instance.props = finishedWork.memoizedProps;
|
||||
instance.state = finishedWork.memoizedState;
|
||||
instance.componentDidUpdate(prevProps, prevState);
|
||||
if (__DEV__) {
|
||||
stopPhaseTimer();
|
||||
}
|
||||
stopPhaseTimer();
|
||||
}
|
||||
}
|
||||
const updateQueue = finishedWork.updateQueue;
|
||||
|
||||
+6
-8
@@ -20,9 +20,7 @@ import checkPropTypes from 'prop-types/checkPropTypes';
|
||||
|
||||
import {createCursor, pop, push} from './ReactFiberStack';
|
||||
import ReactDebugCurrentFiber from './ReactDebugCurrentFiber';
|
||||
import ReactDebugFiberPerf from './ReactDebugFiberPerf';
|
||||
|
||||
const {startPhaseTimer, stopPhaseTimer} = ReactDebugFiberPerf;
|
||||
import {startPhaseTimer, stopPhaseTimer} from './ReactDebugFiberPerf';
|
||||
|
||||
if (__DEV__) {
|
||||
var warnedAboutMissingGetChildContext = {};
|
||||
@@ -177,12 +175,12 @@ export function processChildContext(
|
||||
let childContext;
|
||||
if (__DEV__) {
|
||||
ReactDebugCurrentFiber.setCurrentPhase('getChildContext');
|
||||
startPhaseTimer(fiber, 'getChildContext');
|
||||
childContext = instance.getChildContext();
|
||||
stopPhaseTimer();
|
||||
}
|
||||
startPhaseTimer(fiber, 'getChildContext');
|
||||
childContext = instance.getChildContext();
|
||||
stopPhaseTimer();
|
||||
if (__DEV__) {
|
||||
ReactDebugCurrentFiber.setCurrentPhase(null);
|
||||
} else {
|
||||
childContext = instance.getChildContext();
|
||||
}
|
||||
for (let contextKey in childContext) {
|
||||
invariant(
|
||||
|
||||
+34
-63
@@ -46,7 +46,21 @@ import ReactFiberHostContext from './ReactFiberHostContext';
|
||||
import ReactFiberHydrationContext from './ReactFiberHydrationContext';
|
||||
import ReactFiberInstrumentation from './ReactFiberInstrumentation';
|
||||
import ReactDebugCurrentFiber from './ReactDebugCurrentFiber';
|
||||
import ReactDebugFiberPerf from './ReactDebugFiberPerf';
|
||||
import {
|
||||
recordEffect,
|
||||
recordScheduleUpdate,
|
||||
startWorkTimer,
|
||||
stopWorkTimer,
|
||||
stopFailedWorkTimer,
|
||||
startWorkLoopTimer,
|
||||
stopWorkLoopTimer,
|
||||
startCommitTimer,
|
||||
stopCommitTimer,
|
||||
startCommitHostEffectsTimer,
|
||||
stopCommitHostEffectsTimer,
|
||||
startCommitLifeCyclesTimer,
|
||||
stopCommitLifeCyclesTimer,
|
||||
} from './ReactDebugFiberPerf';
|
||||
import {popContextProvider} from './ReactFiberContext';
|
||||
import {reset} from './ReactFiberStack';
|
||||
import {logCapturedError} from './ReactFiberErrorLogger';
|
||||
@@ -64,21 +78,6 @@ import {getUpdateExpirationTime} from './ReactFiberUpdateQueue';
|
||||
import {resetContext} from './ReactFiberContext';
|
||||
|
||||
var {invokeGuardedCallback, hasCaughtError, clearCaughtError} = ReactErrorUtils;
|
||||
var {
|
||||
recordEffect,
|
||||
recordScheduleUpdate,
|
||||
startWorkTimer,
|
||||
stopWorkTimer,
|
||||
stopFailedWorkTimer,
|
||||
startWorkLoopTimer,
|
||||
stopWorkLoopTimer,
|
||||
startCommitTimer,
|
||||
stopCommitTimer,
|
||||
startCommitHostEffectsTimer,
|
||||
stopCommitHostEffectsTimer,
|
||||
startCommitLifeCyclesTimer,
|
||||
stopCommitLifeCyclesTimer,
|
||||
} = ReactDebugFiberPerf;
|
||||
|
||||
export type CapturedError = {
|
||||
componentName: ?string,
|
||||
@@ -229,8 +228,8 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
while (nextEffect !== null) {
|
||||
if (__DEV__) {
|
||||
ReactDebugCurrentFiber.setCurrentFiber(nextEffect);
|
||||
recordEffect();
|
||||
}
|
||||
recordEffect();
|
||||
|
||||
const effectTag = nextEffect.effectTag;
|
||||
if (effectTag & ContentReset) {
|
||||
@@ -298,24 +297,18 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
const effectTag = nextEffect.effectTag;
|
||||
|
||||
if (effectTag & (Update | Callback)) {
|
||||
if (__DEV__) {
|
||||
recordEffect();
|
||||
}
|
||||
recordEffect();
|
||||
const current = nextEffect.alternate;
|
||||
commitLifeCycles(current, nextEffect);
|
||||
}
|
||||
|
||||
if (effectTag & Ref) {
|
||||
if (__DEV__) {
|
||||
recordEffect();
|
||||
}
|
||||
recordEffect();
|
||||
commitAttachRef(nextEffect);
|
||||
}
|
||||
|
||||
if (effectTag & Err) {
|
||||
if (__DEV__) {
|
||||
recordEffect();
|
||||
}
|
||||
recordEffect();
|
||||
commitErrorHandling(nextEffect);
|
||||
}
|
||||
|
||||
@@ -338,9 +331,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
// captured elsewhere, to prevent the unmount from being interrupted.
|
||||
isWorking = true;
|
||||
isCommitting = true;
|
||||
if (__DEV__) {
|
||||
startCommitTimer();
|
||||
}
|
||||
startCommitTimer();
|
||||
|
||||
const root: FiberRoot = finishedWork.stateNode;
|
||||
invariant(
|
||||
@@ -377,9 +368,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
// The first pass performs all the host insertions, updates, deletions and
|
||||
// ref unmounts.
|
||||
nextEffect = firstEffect;
|
||||
if (__DEV__) {
|
||||
startCommitHostEffectsTimer();
|
||||
}
|
||||
startCommitHostEffectsTimer();
|
||||
while (nextEffect !== null) {
|
||||
let didError = false;
|
||||
let error;
|
||||
@@ -410,9 +399,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (__DEV__) {
|
||||
stopCommitHostEffectsTimer();
|
||||
}
|
||||
stopCommitHostEffectsTimer();
|
||||
|
||||
resetAfterCommit();
|
||||
|
||||
@@ -427,9 +414,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
// and deletions in the entire tree have already been invoked.
|
||||
// This pass also triggers any renderer-specific initial effects.
|
||||
nextEffect = firstEffect;
|
||||
if (__DEV__) {
|
||||
startCommitLifeCyclesTimer();
|
||||
}
|
||||
startCommitLifeCyclesTimer();
|
||||
while (nextEffect !== null) {
|
||||
let didError = false;
|
||||
let error;
|
||||
@@ -462,10 +447,8 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
|
||||
isCommitting = false;
|
||||
isWorking = false;
|
||||
if (__DEV__) {
|
||||
stopCommitLifeCyclesTimer();
|
||||
stopCommitTimer();
|
||||
}
|
||||
stopCommitLifeCyclesTimer();
|
||||
stopCommitTimer();
|
||||
if (typeof onCommitRoot === 'function') {
|
||||
onCommitRoot(finishedWork.stateNode);
|
||||
}
|
||||
@@ -551,9 +534,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
resetExpirationTime(workInProgress, nextRenderExpirationTime);
|
||||
|
||||
if (next !== null) {
|
||||
if (__DEV__) {
|
||||
stopWorkTimer(workInProgress);
|
||||
}
|
||||
stopWorkTimer(workInProgress);
|
||||
if (__DEV__ && ReactFiberInstrumentation.debugTool) {
|
||||
ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
|
||||
}
|
||||
@@ -595,9 +576,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
}
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
stopWorkTimer(workInProgress);
|
||||
}
|
||||
stopWorkTimer(workInProgress);
|
||||
if (__DEV__ && ReactFiberInstrumentation.debugTool) {
|
||||
ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
|
||||
}
|
||||
@@ -631,8 +610,8 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
const current = workInProgress.alternate;
|
||||
|
||||
// See if beginning this work spawns more work.
|
||||
startWorkTimer(workInProgress);
|
||||
if (__DEV__) {
|
||||
startWorkTimer(workInProgress);
|
||||
ReactDebugCurrentFiber.setCurrentFiber(workInProgress);
|
||||
}
|
||||
let next = beginWork(current, workInProgress, nextRenderExpirationTime);
|
||||
@@ -661,8 +640,8 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
const current = workInProgress.alternate;
|
||||
|
||||
// See if beginning this work spawns more work.
|
||||
startWorkTimer(workInProgress);
|
||||
if (__DEV__) {
|
||||
startWorkTimer(workInProgress);
|
||||
ReactDebugCurrentFiber.setCurrentFiber(workInProgress);
|
||||
}
|
||||
let next = beginFailedWork(
|
||||
@@ -773,9 +752,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
root: FiberRoot,
|
||||
expirationTime: ExpirationTime,
|
||||
): Fiber | null {
|
||||
if (__DEV__) {
|
||||
startWorkLoopTimer();
|
||||
}
|
||||
startWorkLoopTimer();
|
||||
|
||||
invariant(
|
||||
!isWorking,
|
||||
@@ -892,9 +869,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
didFatal = false;
|
||||
firstUncaughtError = null;
|
||||
|
||||
if (__DEV__) {
|
||||
stopWorkLoopTimer();
|
||||
}
|
||||
stopWorkLoopTimer();
|
||||
|
||||
if (uncaughtError !== null) {
|
||||
onUncaughtError(uncaughtError);
|
||||
@@ -1131,11 +1106,9 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
break;
|
||||
}
|
||||
if (node === to || node.alternate === to) {
|
||||
if (__DEV__) {
|
||||
stopFailedWorkTimer(node);
|
||||
}
|
||||
stopFailedWorkTimer(node);
|
||||
break;
|
||||
} else if (__DEV__) {
|
||||
} else {
|
||||
stopWorkTimer(node);
|
||||
}
|
||||
node = node.return;
|
||||
@@ -1190,9 +1163,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
|
||||
expirationTime: ExpirationTime,
|
||||
isErrorRecovery: boolean,
|
||||
) {
|
||||
if (__DEV__) {
|
||||
recordScheduleUpdate();
|
||||
}
|
||||
recordScheduleUpdate();
|
||||
|
||||
if (__DEV__) {
|
||||
if (!isErrorRecovery && fiber.tag === ClassComponent) {
|
||||
|
||||
@@ -7,12 +7,15 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
export const enableAsyncSubtreeAPI = true;
|
||||
export const enableAsyncSchedulingByDefaultInReactDOM = false;
|
||||
// Exports React.Fragment
|
||||
export const enableReactFragment = false;
|
||||
// Exports ReactDOM.createRoot
|
||||
export const enableCreateRoot = false;
|
||||
export const enableUserTimingAPI = __DEV__;
|
||||
|
||||
// Mutating mode (React DOM, React ART, React Native):
|
||||
export const enableMutatingReconciler = true;
|
||||
@@ -20,3 +23,8 @@ export const enableMutatingReconciler = true;
|
||||
export const enableNoopReconciler = false;
|
||||
// Experimental persistent mode (CS):
|
||||
export const enablePersistentReconciler = false;
|
||||
|
||||
// Only used in www builds.
|
||||
export function addUserTimingListener() {
|
||||
invariant(false, 'Not implemented.');
|
||||
}
|
||||
|
||||
@@ -16,11 +16,38 @@ export const {
|
||||
enableAsyncSchedulingByDefaultInReactDOM,
|
||||
enableReactFragment,
|
||||
enableCreateRoot,
|
||||
// Reconciler flags
|
||||
enableMutatingReconciler,
|
||||
enableNoopReconciler,
|
||||
enablePersistentReconciler,
|
||||
} = require('ReactFeatureFlags');
|
||||
|
||||
export let enableUserTimingAPI = __DEV__;
|
||||
|
||||
let refCount = 0;
|
||||
export function addUserTimingListener() {
|
||||
if (__DEV__) {
|
||||
// Noop.
|
||||
return () => {};
|
||||
}
|
||||
refCount++;
|
||||
updateFlagOutsideOfReactCallStack();
|
||||
return () => {
|
||||
refCount--;
|
||||
updateFlagOutsideOfReactCallStack();
|
||||
};
|
||||
}
|
||||
|
||||
let timeout = null;
|
||||
function updateFlagOutsideOfReactCallStack() {
|
||||
if (!timeout) {
|
||||
timeout = setTimeout(() => {
|
||||
timeout = null;
|
||||
enableUserTimingAPI = refCount > 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
type Check<_X, Y: _X, X: Y=_X> = null;
|
||||
|
||||
Reference in New Issue
Block a user