mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Add experimental DebugTracing logger for internal use (#18531)
This commit is contained in:
@@ -39,6 +39,56 @@ describe('ReactDOMServerIntegration', () => {
|
||||
resetModules();
|
||||
});
|
||||
|
||||
// Test pragmas don't support itRenders abstraction
|
||||
if (
|
||||
__EXPERIMENTAL__ &&
|
||||
require('shared/ReactFeatureFlags').enableDebugTracing
|
||||
) {
|
||||
describe('React.unstable_DebugTracingMode', () => {
|
||||
beforeEach(() => {
|
||||
spyOnDevAndProd(console, 'log');
|
||||
});
|
||||
|
||||
itRenders('with one child', async render => {
|
||||
const e = await render(
|
||||
<React.unstable_DebugTracingMode>
|
||||
<div>text1</div>
|
||||
</React.unstable_DebugTracingMode>,
|
||||
);
|
||||
const parent = e.parentNode;
|
||||
expect(parent.childNodes[0].tagName).toBe('DIV');
|
||||
});
|
||||
|
||||
itRenders('mode with several children', async render => {
|
||||
const Header = props => {
|
||||
return <p>header</p>;
|
||||
};
|
||||
const Footer = props => {
|
||||
return (
|
||||
<React.unstable_DebugTracingMode>
|
||||
<h2>footer</h2>
|
||||
<h3>about</h3>
|
||||
</React.unstable_DebugTracingMode>
|
||||
);
|
||||
};
|
||||
const e = await render(
|
||||
<React.unstable_DebugTracingMode>
|
||||
<div>text1</div>
|
||||
<span>text2</span>
|
||||
<Header />
|
||||
<Footer />
|
||||
</React.unstable_DebugTracingMode>,
|
||||
);
|
||||
const parent = e.parentNode;
|
||||
expect(parent.childNodes[0].tagName).toBe('DIV');
|
||||
expect(parent.childNodes[1].tagName).toBe('SPAN');
|
||||
expect(parent.childNodes[2].tagName).toBe('P');
|
||||
expect(parent.childNodes[3].tagName).toBe('H2');
|
||||
expect(parent.childNodes[4].tagName).toBe('H3');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('React.StrictMode', () => {
|
||||
itRenders('a strict mode with one child', async render => {
|
||||
const e = await render(
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import {
|
||||
REACT_DEBUG_TRACING_MODE_TYPE,
|
||||
REACT_FORWARD_REF_TYPE,
|
||||
REACT_FRAGMENT_TYPE,
|
||||
REACT_STRICT_MODE_TYPE,
|
||||
@@ -1002,6 +1003,7 @@ class ReactDOMServerRenderer {
|
||||
}
|
||||
|
||||
switch (elementType) {
|
||||
case REACT_DEBUG_TRACING_MODE_TYPE:
|
||||
case REACT_STRICT_MODE_TYPE:
|
||||
case REACT_PROFILER_TYPE:
|
||||
case REACT_SUSPENSE_LIST_TYPE:
|
||||
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {Wakeable} from 'shared/ReactTypes';
|
||||
|
||||
import {enableDebugTracing} from 'shared/ReactFeatureFlags';
|
||||
|
||||
const nativeConsole: Object = console;
|
||||
let nativeConsoleLog: null | Function = null;
|
||||
|
||||
const pendingGroupArgs: Array<any> = [];
|
||||
let printedGroupIndex: number = -1;
|
||||
|
||||
function group(...groupArgs): void {
|
||||
pendingGroupArgs.push(groupArgs);
|
||||
|
||||
if (nativeConsoleLog === null) {
|
||||
nativeConsoleLog = nativeConsole.log;
|
||||
nativeConsole.log = log;
|
||||
}
|
||||
}
|
||||
|
||||
function groupEnd(): void {
|
||||
pendingGroupArgs.pop();
|
||||
while (printedGroupIndex >= pendingGroupArgs.length) {
|
||||
nativeConsole.groupEnd();
|
||||
printedGroupIndex--;
|
||||
}
|
||||
|
||||
if (pendingGroupArgs.length === 0) {
|
||||
nativeConsole.log = nativeConsoleLog;
|
||||
nativeConsoleLog = null;
|
||||
}
|
||||
}
|
||||
|
||||
function log(...logArgs): void {
|
||||
if (printedGroupIndex < pendingGroupArgs.length - 1) {
|
||||
for (let i = printedGroupIndex + 1; i < pendingGroupArgs.length; i++) {
|
||||
const groupArgs = pendingGroupArgs[i];
|
||||
nativeConsole.group(...groupArgs);
|
||||
}
|
||||
printedGroupIndex = pendingGroupArgs.length - 1;
|
||||
}
|
||||
if (typeof nativeConsoleLog === 'function') {
|
||||
nativeConsoleLog(...logArgs);
|
||||
} else {
|
||||
nativeConsole.log(...logArgs);
|
||||
}
|
||||
}
|
||||
|
||||
const REACT_LOGO_STYLE =
|
||||
'background-color: #20232a; color: #61dafb; padding: 0 2px;';
|
||||
|
||||
export function logCommitStarted(priorityLabel: string): void {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
group(
|
||||
`%c⚛️%c commit%c (priority: ${priorityLabel})`,
|
||||
REACT_LOGO_STYLE,
|
||||
'',
|
||||
'font-weight: normal;',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function logCommitStopped(): void {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
groupEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
|
||||
// $FlowFixMe: Flow cannot handle polymorphic WeakMaps
|
||||
const wakeableIDs: WeakMap<Wakeable, number> = new PossiblyWeakMap();
|
||||
let wakeableID: number = 0;
|
||||
function getWakeableID(wakeable: Wakeable): number {
|
||||
if (!wakeableIDs.has(wakeable)) {
|
||||
wakeableIDs.set(wakeable, wakeableID++);
|
||||
}
|
||||
return ((wakeableIDs.get(wakeable): any): number);
|
||||
}
|
||||
|
||||
export function logComponentSuspended(
|
||||
componentName: string,
|
||||
wakeable: Wakeable,
|
||||
): void {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
const id = getWakeableID(wakeable);
|
||||
const display = (wakeable: any).displayName || wakeable;
|
||||
log(
|
||||
`%c⚛️%c ${componentName} suspended`,
|
||||
REACT_LOGO_STYLE,
|
||||
'color: #80366d; font-weight: bold;',
|
||||
id,
|
||||
display,
|
||||
);
|
||||
wakeable.then(
|
||||
() => {
|
||||
log(
|
||||
`%c⚛️%c ${componentName} resolved`,
|
||||
REACT_LOGO_STYLE,
|
||||
'color: #80366d; font-weight: bold;',
|
||||
id,
|
||||
display,
|
||||
);
|
||||
},
|
||||
() => {
|
||||
log(
|
||||
`%c⚛️%c ${componentName} rejected`,
|
||||
REACT_LOGO_STYLE,
|
||||
'color: #80366d; font-weight: bold;',
|
||||
id,
|
||||
display,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function logLayoutEffectsStarted(priorityLabel: string): void {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
group(
|
||||
`%c⚛️%c layout effects%c (priority: ${priorityLabel})`,
|
||||
REACT_LOGO_STYLE,
|
||||
'',
|
||||
'font-weight: normal;',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function logLayoutEffectsStopped(): void {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
groupEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function logPassiveEffectsStarted(priorityLabel: string): void {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
group(
|
||||
`%c⚛️%c passive effects%c (priority: ${priorityLabel})`,
|
||||
REACT_LOGO_STYLE,
|
||||
'',
|
||||
'font-weight: normal;',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function logPassiveEffectsStopped(): void {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
groupEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function logRenderStarted(priorityLabel: string): void {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
group(
|
||||
`%c⚛️%c render%c (priority: ${priorityLabel})`,
|
||||
REACT_LOGO_STYLE,
|
||||
'',
|
||||
'font-weight: normal;',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function logRenderStopped(): void {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
groupEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function logForceUpdateScheduled(
|
||||
componentName: string,
|
||||
priorityLabel: string,
|
||||
): void {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
log(
|
||||
`%c⚛️%c ${componentName} forced update %c(priority: ${priorityLabel})`,
|
||||
REACT_LOGO_STYLE,
|
||||
'color: #db2e1f; font-weight: bold;',
|
||||
'',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function logStateUpdateScheduled(
|
||||
componentName: string,
|
||||
priorityLabel: string,
|
||||
payloadOrAction: any,
|
||||
): void {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
log(
|
||||
`%c⚛️%c ${componentName} updated state %c(priority: ${priorityLabel})`,
|
||||
REACT_LOGO_STYLE,
|
||||
'color: #01a252; font-weight: bold;',
|
||||
'',
|
||||
payloadOrAction,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ import {NoWork} from './ReactFiberExpirationTime.new';
|
||||
import {
|
||||
NoMode,
|
||||
ConcurrentMode,
|
||||
DebugTracingMode,
|
||||
ProfileMode,
|
||||
StrictMode,
|
||||
BlockingMode,
|
||||
@@ -74,6 +75,7 @@ import {
|
||||
import {
|
||||
REACT_FORWARD_REF_TYPE,
|
||||
REACT_FRAGMENT_TYPE,
|
||||
REACT_DEBUG_TRACING_MODE_TYPE,
|
||||
REACT_STRICT_MODE_TYPE,
|
||||
REACT_PROFILER_TYPE,
|
||||
REACT_PROVIDER_TYPE,
|
||||
@@ -488,6 +490,10 @@ export function createFiberFromTypeAndProps(
|
||||
expirationTime,
|
||||
key,
|
||||
);
|
||||
case REACT_DEBUG_TRACING_MODE_TYPE:
|
||||
fiberTag = Mode;
|
||||
mode |= DebugTracingMode;
|
||||
break;
|
||||
case REACT_STRICT_MODE_TYPE:
|
||||
fiberTag = Mode;
|
||||
mode |= StrictMode;
|
||||
|
||||
@@ -67,6 +67,7 @@ import {NoWork} from './ReactFiberExpirationTime.old';
|
||||
import {
|
||||
NoMode,
|
||||
ConcurrentMode,
|
||||
DebugTracingMode,
|
||||
ProfileMode,
|
||||
StrictMode,
|
||||
BlockingMode,
|
||||
@@ -74,6 +75,7 @@ import {
|
||||
import {
|
||||
REACT_FORWARD_REF_TYPE,
|
||||
REACT_FRAGMENT_TYPE,
|
||||
REACT_DEBUG_TRACING_MODE_TYPE,
|
||||
REACT_STRICT_MODE_TYPE,
|
||||
REACT_PROFILER_TYPE,
|
||||
REACT_PROVIDER_TYPE,
|
||||
@@ -488,6 +490,10 @@ export function createFiberFromTypeAndProps(
|
||||
expirationTime,
|
||||
key,
|
||||
);
|
||||
case REACT_DEBUG_TRACING_MODE_TYPE:
|
||||
fiberTag = Mode;
|
||||
mode |= DebugTracingMode;
|
||||
break;
|
||||
case REACT_STRICT_MODE_TYPE:
|
||||
fiberTag = Mode;
|
||||
mode |= StrictMode;
|
||||
|
||||
@@ -10,12 +10,14 @@
|
||||
import type {Fiber} from './ReactInternalTypes';
|
||||
import type {ExpirationTime} from './ReactFiberExpirationTime.new';
|
||||
import type {UpdateQueue} from './ReactUpdateQueue.new';
|
||||
import type {ReactPriorityLevel} from './ReactInternalTypes';
|
||||
|
||||
import * as React from 'react';
|
||||
import {Update, Snapshot} from './ReactSideEffectTags';
|
||||
import {
|
||||
debugRenderPhaseSideEffectsForStrictMode,
|
||||
disableLegacyContext,
|
||||
enableDebugTracing,
|
||||
warnAboutDeprecatedLifecycles,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import ReactStrictModeWarnings from './ReactStrictModeWarnings.new';
|
||||
@@ -27,7 +29,7 @@ import invariant from 'shared/invariant';
|
||||
import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols';
|
||||
|
||||
import {resolveDefaultProps} from './ReactFiberLazyComponent.new';
|
||||
import {StrictMode} from './ReactTypeOfMode';
|
||||
import {DebugTracingMode, StrictMode} from './ReactTypeOfMode';
|
||||
|
||||
import {
|
||||
enqueueUpdate,
|
||||
@@ -53,8 +55,10 @@ import {
|
||||
requestCurrentTimeForUpdate,
|
||||
computeExpirationForFiber,
|
||||
scheduleUpdateOnFiber,
|
||||
priorityLevelToLabel,
|
||||
} from './ReactFiberWorkLoop.new';
|
||||
import {requestCurrentSuspenseConfig} from './ReactFiberSuspenseConfig';
|
||||
import {logForceUpdateScheduled, logStateUpdateScheduled} from './DebugTracing';
|
||||
|
||||
import {disableLogs, reenableLogs} from 'shared/ConsolePatchingDev';
|
||||
|
||||
@@ -207,6 +211,18 @@ const classComponentUpdater = {
|
||||
|
||||
enqueueUpdate(fiber, update);
|
||||
scheduleUpdateOnFiber(fiber, expirationTime);
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
if (fiber.mode & DebugTracingMode) {
|
||||
const label = priorityLevelToLabel(
|
||||
((update.priority: any): ReactPriorityLevel),
|
||||
);
|
||||
const name = getComponentName(fiber.type) || 'Unknown';
|
||||
logStateUpdateScheduled(name, label, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
enqueueReplaceState(inst, payload, callback) {
|
||||
const fiber = getInstance(inst);
|
||||
@@ -231,6 +247,18 @@ const classComponentUpdater = {
|
||||
|
||||
enqueueUpdate(fiber, update);
|
||||
scheduleUpdateOnFiber(fiber, expirationTime);
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
if (fiber.mode & DebugTracingMode) {
|
||||
const label = priorityLevelToLabel(
|
||||
((update.priority: any): ReactPriorityLevel),
|
||||
);
|
||||
const name = getComponentName(fiber.type) || 'Unknown';
|
||||
logStateUpdateScheduled(name, label, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
enqueueForceUpdate(inst, callback) {
|
||||
const fiber = getInstance(inst);
|
||||
@@ -254,6 +282,18 @@ const classComponentUpdater = {
|
||||
|
||||
enqueueUpdate(fiber, update);
|
||||
scheduleUpdateOnFiber(fiber, expirationTime);
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
if (fiber.mode & DebugTracingMode) {
|
||||
const label = priorityLevelToLabel(
|
||||
((update.priority: any): ReactPriorityLevel),
|
||||
);
|
||||
const name = getComponentName(fiber.type) || 'Unknown';
|
||||
logForceUpdateScheduled(name, label);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -10,12 +10,14 @@
|
||||
import type {Fiber} from './ReactInternalTypes';
|
||||
import type {ExpirationTime} from './ReactFiberExpirationTime.old';
|
||||
import type {UpdateQueue} from './ReactUpdateQueue.old';
|
||||
import type {ReactPriorityLevel} from './ReactInternalTypes';
|
||||
|
||||
import * as React from 'react';
|
||||
import {Update, Snapshot} from './ReactSideEffectTags';
|
||||
import {
|
||||
debugRenderPhaseSideEffectsForStrictMode,
|
||||
disableLegacyContext,
|
||||
enableDebugTracing,
|
||||
warnAboutDeprecatedLifecycles,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import ReactStrictModeWarnings from './ReactStrictModeWarnings.old';
|
||||
@@ -27,7 +29,7 @@ import invariant from 'shared/invariant';
|
||||
import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols';
|
||||
|
||||
import {resolveDefaultProps} from './ReactFiberLazyComponent.old';
|
||||
import {StrictMode} from './ReactTypeOfMode';
|
||||
import {DebugTracingMode, StrictMode} from './ReactTypeOfMode';
|
||||
|
||||
import {
|
||||
enqueueUpdate,
|
||||
@@ -53,8 +55,10 @@ import {
|
||||
requestCurrentTimeForUpdate,
|
||||
computeExpirationForFiber,
|
||||
scheduleUpdateOnFiber,
|
||||
priorityLevelToLabel,
|
||||
} from './ReactFiberWorkLoop.old';
|
||||
import {requestCurrentSuspenseConfig} from './ReactFiberSuspenseConfig';
|
||||
import {logForceUpdateScheduled, logStateUpdateScheduled} from './DebugTracing';
|
||||
|
||||
import {disableLogs, reenableLogs} from 'shared/ConsolePatchingDev';
|
||||
|
||||
@@ -207,6 +211,18 @@ const classComponentUpdater = {
|
||||
|
||||
enqueueUpdate(fiber, update);
|
||||
scheduleUpdateOnFiber(fiber, expirationTime);
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
if (fiber.mode & DebugTracingMode) {
|
||||
const label = priorityLevelToLabel(
|
||||
((update.priority: any): ReactPriorityLevel),
|
||||
);
|
||||
const name = getComponentName(fiber.type) || 'Unknown';
|
||||
logStateUpdateScheduled(name, label, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
enqueueReplaceState(inst, payload, callback) {
|
||||
const fiber = getInstance(inst);
|
||||
@@ -231,6 +247,18 @@ const classComponentUpdater = {
|
||||
|
||||
enqueueUpdate(fiber, update);
|
||||
scheduleUpdateOnFiber(fiber, expirationTime);
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
if (fiber.mode & DebugTracingMode) {
|
||||
const label = priorityLevelToLabel(
|
||||
((update.priority: any): ReactPriorityLevel),
|
||||
);
|
||||
const name = getComponentName(fiber.type) || 'Unknown';
|
||||
logStateUpdateScheduled(name, label, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
enqueueForceUpdate(inst, callback) {
|
||||
const fiber = getInstance(inst);
|
||||
@@ -254,6 +282,18 @@ const classComponentUpdater = {
|
||||
|
||||
enqueueUpdate(fiber, update);
|
||||
scheduleUpdateOnFiber(fiber, expirationTime);
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
if (fiber.mode & DebugTracingMode) {
|
||||
const label = priorityLevelToLabel(
|
||||
((update.priority: any): ReactPriorityLevel),
|
||||
);
|
||||
const name = getComponentName(fiber.type) || 'Unknown';
|
||||
logForceUpdateScheduled(name, label);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -30,11 +30,15 @@ import type {
|
||||
} from './ReactFiberHostConfig';
|
||||
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import {enableUseEventAPI} from 'shared/ReactFeatureFlags';
|
||||
import {enableDebugTracing, enableUseEventAPI} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import {markRootExpiredAtTime} from './ReactFiberRoot.new';
|
||||
import {NoWork, Sync} from './ReactFiberExpirationTime.new';
|
||||
import {NoMode, BlockingMode} from './ReactTypeOfMode';
|
||||
import {
|
||||
inferPriorityFromExpirationTime,
|
||||
NoWork,
|
||||
Sync,
|
||||
} from './ReactFiberExpirationTime.new';
|
||||
import {NoMode, BlockingMode, DebugTracingMode} from './ReactTypeOfMode';
|
||||
import {readContext} from './ReactFiberNewContext.new';
|
||||
import {createDeprecatedResponderListener} from './ReactFiberDeprecatedEvents.new';
|
||||
import {
|
||||
@@ -57,6 +61,7 @@ import {
|
||||
warnIfNotScopedWithMatchingAct,
|
||||
markRenderEventTimeAndConfig,
|
||||
markUnprocessedUpdateTime,
|
||||
priorityLevelToLabel,
|
||||
} from './ReactFiberWorkLoop.new';
|
||||
import {
|
||||
registerEvent,
|
||||
@@ -92,6 +97,7 @@ import {
|
||||
} from './ReactMutableSource.new';
|
||||
import {getRootHostContainer} from './ReactFiberHostContext.new';
|
||||
import {getIsRendering} from './ReactCurrentFiber';
|
||||
import {logStateUpdateScheduled} from './DebugTracing';
|
||||
|
||||
const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals;
|
||||
|
||||
@@ -1738,6 +1744,20 @@ function dispatchAction<S, A>(
|
||||
}
|
||||
scheduleUpdateOnFiber(fiber, expirationTime);
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
if (fiber.mode & DebugTracingMode) {
|
||||
const priorityLevel = inferPriorityFromExpirationTime(
|
||||
currentTime,
|
||||
expirationTime,
|
||||
);
|
||||
const label = priorityLevelToLabel(priorityLevel);
|
||||
const name = getComponentName(fiber.type) || 'Unknown';
|
||||
logStateUpdateScheduled(name, label, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const noOpMount = () => {};
|
||||
|
||||
@@ -30,11 +30,15 @@ import type {
|
||||
} from './ReactFiberHostConfig';
|
||||
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import {enableUseEventAPI} from 'shared/ReactFeatureFlags';
|
||||
import {enableDebugTracing, enableUseEventAPI} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import {markRootExpiredAtTime} from './ReactFiberRoot.old';
|
||||
import {NoWork, Sync} from './ReactFiberExpirationTime.old';
|
||||
import {NoMode, BlockingMode} from './ReactTypeOfMode';
|
||||
import {NoMode, BlockingMode, DebugTracingMode} from './ReactTypeOfMode';
|
||||
import {
|
||||
inferPriorityFromExpirationTime,
|
||||
NoWork,
|
||||
Sync,
|
||||
} from './ReactFiberExpirationTime.old';
|
||||
import {readContext} from './ReactFiberNewContext.old';
|
||||
import {createDeprecatedResponderListener} from './ReactFiberDeprecatedEvents.old';
|
||||
import {
|
||||
@@ -57,6 +61,7 @@ import {
|
||||
warnIfNotScopedWithMatchingAct,
|
||||
markRenderEventTimeAndConfig,
|
||||
markUnprocessedUpdateTime,
|
||||
priorityLevelToLabel,
|
||||
} from './ReactFiberWorkLoop.old';
|
||||
import {
|
||||
registerEvent,
|
||||
@@ -92,6 +97,7 @@ import {
|
||||
} from './ReactMutableSource.old';
|
||||
import {getRootHostContainer} from './ReactFiberHostContext.old';
|
||||
import {getIsRendering} from './ReactCurrentFiber';
|
||||
import {logStateUpdateScheduled} from './DebugTracing';
|
||||
|
||||
const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals;
|
||||
|
||||
@@ -1733,6 +1739,20 @@ function dispatchAction<S, A>(
|
||||
}
|
||||
scheduleUpdateOnFiber(fiber, expirationTime);
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
if (fiber.mode & DebugTracingMode) {
|
||||
const priorityLevel = inferPriorityFromExpirationTime(
|
||||
currentTime,
|
||||
expirationTime,
|
||||
);
|
||||
const label = priorityLevelToLabel(priorityLevel);
|
||||
const name = getComponentName(fiber.type) || 'Unknown';
|
||||
logStateUpdateScheduled(name, label, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const noOpMount = () => {};
|
||||
|
||||
@@ -36,7 +36,6 @@ import {
|
||||
import getComponentName from 'shared/getComponentName';
|
||||
import invariant from 'shared/invariant';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
|
||||
import {getPublicInstance} from './ReactFiberHostConfig';
|
||||
import {
|
||||
findCurrentUnmaskedContext,
|
||||
|
||||
@@ -36,7 +36,6 @@ import {
|
||||
import getComponentName from 'shared/getComponentName';
|
||||
import invariant from 'shared/invariant';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
|
||||
import {getPublicInstance} from './ReactFiberHostConfig';
|
||||
import {
|
||||
findCurrentUnmaskedContext,
|
||||
|
||||
@@ -29,9 +29,9 @@ import {
|
||||
ShouldCapture,
|
||||
LifecycleEffectMask,
|
||||
} from './ReactSideEffectTags';
|
||||
import {NoMode, BlockingMode} from './ReactTypeOfMode';
|
||||
import {shouldCaptureSuspense} from './ReactFiberSuspenseComponent.new';
|
||||
|
||||
import {NoMode, BlockingMode, DebugTracingMode} from './ReactTypeOfMode';
|
||||
import {enableDebugTracing} from 'shared/ReactFeatureFlags';
|
||||
import {createCapturedValue} from './ReactCapturedValue';
|
||||
import {
|
||||
enqueueCapturedUpdate,
|
||||
@@ -55,6 +55,7 @@ import {
|
||||
pingSuspendedRoot,
|
||||
} from './ReactFiberWorkLoop.new';
|
||||
import {logCapturedError} from './ReactFiberErrorLogger';
|
||||
import {logComponentSuspended} from './DebugTracing';
|
||||
|
||||
import {Sync, NoWork} from './ReactFiberExpirationTime.new';
|
||||
|
||||
@@ -194,6 +195,15 @@ function throwException(
|
||||
// This is a wakeable.
|
||||
const wakeable: Wakeable = (value: any);
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
if (sourceFiber.mode & DebugTracingMode) {
|
||||
const name = getComponentName(sourceFiber.type) || 'Unknown';
|
||||
logComponentSuspended(name, wakeable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((sourceFiber.mode & BlockingMode) === NoMode) {
|
||||
// Reset the memoizedState to what it was before we attempted
|
||||
// to render it.
|
||||
|
||||
@@ -29,9 +29,9 @@ import {
|
||||
ShouldCapture,
|
||||
LifecycleEffectMask,
|
||||
} from './ReactSideEffectTags';
|
||||
import {NoMode, BlockingMode} from './ReactTypeOfMode';
|
||||
import {shouldCaptureSuspense} from './ReactFiberSuspenseComponent.old';
|
||||
|
||||
import {NoMode, BlockingMode, DebugTracingMode} from './ReactTypeOfMode';
|
||||
import {enableDebugTracing} from 'shared/ReactFeatureFlags';
|
||||
import {createCapturedValue} from './ReactCapturedValue';
|
||||
import {
|
||||
enqueueCapturedUpdate,
|
||||
@@ -55,6 +55,7 @@ import {
|
||||
pingSuspendedRoot,
|
||||
} from './ReactFiberWorkLoop.old';
|
||||
import {logCapturedError} from './ReactFiberErrorLogger';
|
||||
import {logComponentSuspended} from './DebugTracing';
|
||||
|
||||
import {Sync} from './ReactFiberExpirationTime.old';
|
||||
|
||||
@@ -194,6 +195,15 @@ function throwException(
|
||||
// This is a wakeable.
|
||||
const wakeable: Wakeable = (value: any);
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
if (sourceFiber.mode & DebugTracingMode) {
|
||||
const name = getComponentName(sourceFiber.type) || 'Unknown';
|
||||
logComponentSuspended(name, wakeable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((sourceFiber.mode & BlockingMode) === NoMode) {
|
||||
// Reset the memoizedState to what it was before we attempted
|
||||
// to render it.
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
enableSchedulerTracing,
|
||||
warnAboutUnmockedScheduler,
|
||||
disableSchedulerTimeoutBasedOnReactExpirationTime,
|
||||
enableDebugTracing,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import invariant from 'shared/invariant';
|
||||
@@ -48,6 +49,16 @@ import {
|
||||
flushSyncCallbackQueue,
|
||||
scheduleSyncCallback,
|
||||
} from './SchedulerWithReactIntegration.new';
|
||||
import {
|
||||
logCommitStarted,
|
||||
logCommitStopped,
|
||||
logLayoutEffectsStarted,
|
||||
logLayoutEffectsStopped,
|
||||
logPassiveEffectsStarted,
|
||||
logPassiveEffectsStopped,
|
||||
logRenderStarted,
|
||||
logRenderStopped,
|
||||
} from './DebugTracing';
|
||||
|
||||
// The scheduler is imported here *only* to detect whether it's been mocked
|
||||
import * as Scheduler from 'scheduler';
|
||||
@@ -378,6 +389,29 @@ export function computeExpirationForFiber(
|
||||
return expirationTime;
|
||||
}
|
||||
|
||||
export function priorityLevelToLabel(
|
||||
priorityLevel: ReactPriorityLevel,
|
||||
): string {
|
||||
if (__DEV__ && enableDebugTracing) {
|
||||
switch (priorityLevel) {
|
||||
case ImmediatePriority:
|
||||
return 'immediate';
|
||||
case UserBlockingPriority:
|
||||
return 'user-blocking';
|
||||
case NormalPriority:
|
||||
return 'normal';
|
||||
case LowPriority:
|
||||
return 'low';
|
||||
case IdlePriority:
|
||||
return 'idle';
|
||||
default:
|
||||
return 'other';
|
||||
}
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function scheduleUpdateOnFiber(
|
||||
fiber: Fiber,
|
||||
expirationTime: ExpirationTime,
|
||||
@@ -1386,6 +1420,15 @@ function renderRootSync(root, expirationTime) {
|
||||
}
|
||||
|
||||
const prevInteractions = pushInteractions(root);
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
const priorityLevel = getCurrentPriorityLevel();
|
||||
const label = priorityLevelToLabel(priorityLevel);
|
||||
logRenderStarted(label);
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
try {
|
||||
workLoopSync();
|
||||
@@ -1411,6 +1454,12 @@ function renderRootSync(root, expirationTime) {
|
||||
);
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logRenderStopped();
|
||||
}
|
||||
}
|
||||
|
||||
// Set this to null to indicate there's no in-progress render.
|
||||
workInProgressRoot = null;
|
||||
|
||||
@@ -1439,6 +1488,15 @@ function renderRootConcurrent(root, expirationTime) {
|
||||
}
|
||||
|
||||
const prevInteractions = pushInteractions(root);
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
const priorityLevel = getCurrentPriorityLevel();
|
||||
const label = priorityLevelToLabel(priorityLevel);
|
||||
logRenderStarted(label);
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
try {
|
||||
workLoopConcurrent();
|
||||
@@ -1455,6 +1513,12 @@ function renderRootConcurrent(root, expirationTime) {
|
||||
popDispatcher(prevDispatcher);
|
||||
executionContext = prevExecutionContext;
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logRenderStopped();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the tree has completed.
|
||||
if (workInProgress !== null) {
|
||||
// Still work remaining.
|
||||
@@ -1722,6 +1786,12 @@ function commitRoot(root) {
|
||||
}
|
||||
|
||||
function commitRootImpl(root, renderPriorityLevel) {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
const label = priorityLevelToLabel(renderPriorityLevel);
|
||||
logCommitStarted(label);
|
||||
}
|
||||
}
|
||||
do {
|
||||
// `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which
|
||||
// means `flushPassiveEffects` will sometimes result in additional
|
||||
@@ -1741,6 +1811,11 @@ function commitRootImpl(root, renderPriorityLevel) {
|
||||
const finishedWork = root.finishedWork;
|
||||
const expirationTime = root.finishedExpirationTime;
|
||||
if (finishedWork === null) {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logCommitStopped();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
root.finishedWork = null;
|
||||
@@ -2021,6 +2096,12 @@ function commitRootImpl(root, renderPriorityLevel) {
|
||||
}
|
||||
|
||||
if ((executionContext & LegacyUnbatchedContext) !== NoContext) {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logCommitStopped();
|
||||
}
|
||||
}
|
||||
|
||||
// This is a legacy edge case. We just committed the initial mount of
|
||||
// a ReactDOM.render-ed root inside of batchedUpdates. The commit fired
|
||||
// synchronously, but layout updates should be deferred until the end
|
||||
@@ -2030,6 +2111,13 @@ function commitRootImpl(root, renderPriorityLevel) {
|
||||
|
||||
// If layout work was scheduled, flush it now.
|
||||
flushSyncCallbackQueue();
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logCommitStopped();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2137,6 +2225,14 @@ function commitLayoutEffects(
|
||||
root: FiberRoot,
|
||||
committedExpirationTime: ExpirationTime,
|
||||
) {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
const priorityLevel = getCurrentPriorityLevel();
|
||||
const label = priorityLevelToLabel(priorityLevel);
|
||||
logLayoutEffectsStarted(label);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Should probably move the bulk of this function to commitWork.
|
||||
while (nextEffect !== null) {
|
||||
setCurrentDebugFiberInDEV(nextEffect);
|
||||
@@ -2160,6 +2256,12 @@ function commitLayoutEffects(
|
||||
resetCurrentDebugFiberInDEV();
|
||||
nextEffect = nextEffect.nextEffect;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logLayoutEffectsStopped();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function flushPassiveEffects() {
|
||||
@@ -2238,6 +2340,14 @@ function flushPassiveEffectsImpl() {
|
||||
'Cannot flush passive effects while already rendering.',
|
||||
);
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
const priorityLevel = getCurrentPriorityLevel();
|
||||
const label = priorityLevelToLabel(priorityLevel);
|
||||
logPassiveEffectsStarted(label);
|
||||
}
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
isFlushingPassiveEffects = true;
|
||||
}
|
||||
@@ -2405,6 +2515,12 @@ function flushPassiveEffectsImpl() {
|
||||
isFlushingPassiveEffects = false;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logPassiveEffectsStopped();
|
||||
}
|
||||
}
|
||||
|
||||
executionContext = prevExecutionContext;
|
||||
|
||||
flushSyncCallbackQueue();
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
enableSchedulerTracing,
|
||||
warnAboutUnmockedScheduler,
|
||||
disableSchedulerTimeoutBasedOnReactExpirationTime,
|
||||
enableDebugTracing,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import invariant from 'shared/invariant';
|
||||
@@ -48,6 +49,16 @@ import {
|
||||
flushSyncCallbackQueue,
|
||||
scheduleSyncCallback,
|
||||
} from './SchedulerWithReactIntegration.old';
|
||||
import {
|
||||
logCommitStarted,
|
||||
logCommitStopped,
|
||||
logLayoutEffectsStarted,
|
||||
logLayoutEffectsStopped,
|
||||
logPassiveEffectsStarted,
|
||||
logPassiveEffectsStopped,
|
||||
logRenderStarted,
|
||||
logRenderStopped,
|
||||
} from './DebugTracing';
|
||||
|
||||
// The scheduler is imported here *only* to detect whether it's been mocked
|
||||
import * as Scheduler from 'scheduler';
|
||||
@@ -371,6 +382,29 @@ export function computeExpirationForFiber(
|
||||
return expirationTime;
|
||||
}
|
||||
|
||||
export function priorityLevelToLabel(
|
||||
priorityLevel: ReactPriorityLevel,
|
||||
): string {
|
||||
if (__DEV__ && enableDebugTracing) {
|
||||
switch (priorityLevel) {
|
||||
case ImmediatePriority:
|
||||
return 'immediate';
|
||||
case UserBlockingPriority:
|
||||
return 'user-blocking';
|
||||
case NormalPriority:
|
||||
return 'normal';
|
||||
case LowPriority:
|
||||
return 'low';
|
||||
case IdlePriority:
|
||||
return 'idle';
|
||||
default:
|
||||
return 'other';
|
||||
}
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function scheduleUpdateOnFiber(
|
||||
fiber: Fiber,
|
||||
expirationTime: ExpirationTime,
|
||||
@@ -1405,6 +1439,15 @@ function renderRootSync(root, expirationTime) {
|
||||
}
|
||||
|
||||
const prevInteractions = pushInteractions(root);
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
const priorityLevel = getCurrentPriorityLevel();
|
||||
const label = priorityLevelToLabel(priorityLevel);
|
||||
logRenderStarted(label);
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
try {
|
||||
workLoopSync();
|
||||
@@ -1430,6 +1473,12 @@ function renderRootSync(root, expirationTime) {
|
||||
);
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logRenderStopped();
|
||||
}
|
||||
}
|
||||
|
||||
// Set this to null to indicate there's no in-progress render.
|
||||
workInProgressRoot = null;
|
||||
|
||||
@@ -1458,6 +1507,15 @@ function renderRootConcurrent(root, expirationTime) {
|
||||
}
|
||||
|
||||
const prevInteractions = pushInteractions(root);
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
const priorityLevel = getCurrentPriorityLevel();
|
||||
const label = priorityLevelToLabel(priorityLevel);
|
||||
logRenderStarted(label);
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
try {
|
||||
workLoopConcurrent();
|
||||
@@ -1474,6 +1532,12 @@ function renderRootConcurrent(root, expirationTime) {
|
||||
popDispatcher(prevDispatcher);
|
||||
executionContext = prevExecutionContext;
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logRenderStopped();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the tree has completed.
|
||||
if (workInProgress !== null) {
|
||||
// Still work remaining.
|
||||
@@ -1741,6 +1805,12 @@ function commitRoot(root) {
|
||||
}
|
||||
|
||||
function commitRootImpl(root, renderPriorityLevel) {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
const label = priorityLevelToLabel(renderPriorityLevel);
|
||||
logCommitStarted(label);
|
||||
}
|
||||
}
|
||||
do {
|
||||
// `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which
|
||||
// means `flushPassiveEffects` will sometimes result in additional
|
||||
@@ -1760,6 +1830,11 @@ function commitRootImpl(root, renderPriorityLevel) {
|
||||
const finishedWork = root.finishedWork;
|
||||
const expirationTime = root.finishedExpirationTime;
|
||||
if (finishedWork === null) {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logCommitStopped();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
root.finishedWork = null;
|
||||
@@ -2040,6 +2115,12 @@ function commitRootImpl(root, renderPriorityLevel) {
|
||||
}
|
||||
|
||||
if ((executionContext & LegacyUnbatchedContext) !== NoContext) {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logCommitStopped();
|
||||
}
|
||||
}
|
||||
|
||||
// This is a legacy edge case. We just committed the initial mount of
|
||||
// a ReactDOM.render-ed root inside of batchedUpdates. The commit fired
|
||||
// synchronously, but layout updates should be deferred until the end
|
||||
@@ -2049,6 +2130,13 @@ function commitRootImpl(root, renderPriorityLevel) {
|
||||
|
||||
// If layout work was scheduled, flush it now.
|
||||
flushSyncCallbackQueue();
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logCommitStopped();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2156,6 +2244,14 @@ function commitLayoutEffects(
|
||||
root: FiberRoot,
|
||||
committedExpirationTime: ExpirationTime,
|
||||
) {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
const priorityLevel = getCurrentPriorityLevel();
|
||||
const label = priorityLevelToLabel(priorityLevel);
|
||||
logLayoutEffectsStarted(label);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Should probably move the bulk of this function to commitWork.
|
||||
while (nextEffect !== null) {
|
||||
setCurrentDebugFiberInDEV(nextEffect);
|
||||
@@ -2179,6 +2275,12 @@ function commitLayoutEffects(
|
||||
resetCurrentDebugFiberInDEV();
|
||||
nextEffect = nextEffect.nextEffect;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logLayoutEffectsStopped();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function flushPassiveEffects() {
|
||||
@@ -2257,6 +2359,14 @@ function flushPassiveEffectsImpl() {
|
||||
'Cannot flush passive effects while already rendering.',
|
||||
);
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
const priorityLevel = getCurrentPriorityLevel();
|
||||
const label = priorityLevelToLabel(priorityLevel);
|
||||
logPassiveEffectsStarted(label);
|
||||
}
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
isFlushingPassiveEffects = true;
|
||||
}
|
||||
@@ -2424,6 +2534,12 @@ function flushPassiveEffectsImpl() {
|
||||
isFlushingPassiveEffects = false;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logPassiveEffectsStopped();
|
||||
}
|
||||
}
|
||||
|
||||
executionContext = prevExecutionContext;
|
||||
|
||||
flushSyncCallbackQueue();
|
||||
|
||||
+6
-5
@@ -9,10 +9,11 @@
|
||||
|
||||
export type TypeOfMode = number;
|
||||
|
||||
export const NoMode = 0b0000;
|
||||
export const StrictMode = 0b0001;
|
||||
export const NoMode = 0b00000;
|
||||
export const StrictMode = 0b00001;
|
||||
// TODO: Remove BlockingMode and ConcurrentMode by reading from the root
|
||||
// tag instead
|
||||
export const BlockingMode = 0b0010;
|
||||
export const ConcurrentMode = 0b0100;
|
||||
export const ProfileMode = 0b1000;
|
||||
export const BlockingMode = 0b00010;
|
||||
export const ConcurrentMode = 0b00100;
|
||||
export const ProfileMode = 0b01000;
|
||||
export const DebugTracingMode = 0b10000;
|
||||
|
||||
@@ -0,0 +1,345 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
describe('DebugTracing', () => {
|
||||
let React;
|
||||
let ReactTestRenderer;
|
||||
let Scheduler;
|
||||
|
||||
let logs;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
React = require('react');
|
||||
ReactTestRenderer = require('react-test-renderer');
|
||||
Scheduler = require('scheduler');
|
||||
|
||||
logs = [];
|
||||
|
||||
const groups = [];
|
||||
|
||||
spyOnDevAndProd(console, 'log').and.callFake(message => {
|
||||
logs.push(`log: ${message.replace(/%c/g, '')}`);
|
||||
});
|
||||
spyOnDevAndProd(console, 'group').and.callFake(message => {
|
||||
logs.push(`group: ${message.replace(/%c/g, '')}`);
|
||||
groups.push(message);
|
||||
});
|
||||
spyOnDevAndProd(console, 'groupEnd').and.callFake(() => {
|
||||
const message = groups.pop();
|
||||
logs.push(`groupEnd: ${message.replace(/%c/g, '')}`);
|
||||
});
|
||||
});
|
||||
|
||||
// @gate experimental
|
||||
it('should not log anything for sync render without suspends or state updates', () => {
|
||||
ReactTestRenderer.create(
|
||||
<React.unstable_DebugTracingMode>
|
||||
<div />
|
||||
</React.unstable_DebugTracingMode>,
|
||||
);
|
||||
|
||||
expect(logs).toEqual([]);
|
||||
});
|
||||
|
||||
// @gate experimental
|
||||
it('should not log anything for concurrent render without suspends or state updates', () => {
|
||||
ReactTestRenderer.create(
|
||||
<React.unstable_DebugTracingMode>
|
||||
<div />
|
||||
</React.unstable_DebugTracingMode>,
|
||||
{unstable_isConcurrent: true},
|
||||
);
|
||||
|
||||
expect(logs).toEqual([]);
|
||||
|
||||
logs.splice(0);
|
||||
|
||||
expect(Scheduler).toFlushUntilNextPaint([]);
|
||||
|
||||
expect(logs).toEqual([]);
|
||||
});
|
||||
|
||||
// @gate experimental && enableDebugTracing
|
||||
it('should log sync render with suspense', async () => {
|
||||
const fakeSuspensPromise = Promise.resolve(true);
|
||||
function Example() {
|
||||
throw fakeSuspensPromise;
|
||||
}
|
||||
|
||||
ReactTestRenderer.create(
|
||||
<React.unstable_DebugTracingMode>
|
||||
<React.Suspense fallback={null}>
|
||||
<Example />
|
||||
</React.Suspense>
|
||||
</React.unstable_DebugTracingMode>,
|
||||
);
|
||||
|
||||
expect(logs).toEqual([
|
||||
'group: ⚛️ render (priority: immediate)',
|
||||
'log: ⚛️ Example suspended',
|
||||
'groupEnd: ⚛️ render (priority: immediate)',
|
||||
]);
|
||||
|
||||
logs.splice(0);
|
||||
|
||||
await fakeSuspensPromise;
|
||||
expect(logs).toEqual(['log: ⚛️ Example resolved']);
|
||||
});
|
||||
|
||||
// @gate experimental && enableDebugTracing
|
||||
it('should log concurrent render with suspense', async () => {
|
||||
const fakeSuspensPromise = Promise.resolve(true);
|
||||
function Example() {
|
||||
throw fakeSuspensPromise;
|
||||
}
|
||||
|
||||
ReactTestRenderer.create(
|
||||
<React.unstable_DebugTracingMode>
|
||||
<React.Suspense fallback={null}>
|
||||
<Example />
|
||||
</React.Suspense>
|
||||
</React.unstable_DebugTracingMode>,
|
||||
{unstable_isConcurrent: true},
|
||||
);
|
||||
|
||||
expect(logs).toEqual([]);
|
||||
|
||||
logs.splice(0);
|
||||
|
||||
expect(Scheduler).toFlushUntilNextPaint([]);
|
||||
|
||||
expect(logs).toEqual([
|
||||
'group: ⚛️ render (priority: normal)',
|
||||
'log: ⚛️ Example suspended',
|
||||
'groupEnd: ⚛️ render (priority: normal)',
|
||||
]);
|
||||
|
||||
logs.splice(0);
|
||||
|
||||
await fakeSuspensPromise;
|
||||
expect(logs).toEqual(['log: ⚛️ Example resolved']);
|
||||
});
|
||||
|
||||
// @gate experimental && enableDebugTracing
|
||||
it('should log cascading class component updates', () => {
|
||||
class Example extends React.Component {
|
||||
state = {didMount: false};
|
||||
componentDidMount() {
|
||||
this.setState({didMount: true});
|
||||
}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
ReactTestRenderer.create(
|
||||
<React.unstable_DebugTracingMode>
|
||||
<Example />
|
||||
</React.unstable_DebugTracingMode>,
|
||||
{unstable_isConcurrent: true},
|
||||
);
|
||||
|
||||
expect(logs).toEqual([]);
|
||||
|
||||
logs.splice(0);
|
||||
|
||||
expect(Scheduler).toFlushUntilNextPaint([]);
|
||||
|
||||
expect(logs).toEqual([
|
||||
'group: ⚛️ commit (priority: normal)',
|
||||
'group: ⚛️ layout effects (priority: immediate)',
|
||||
'log: ⚛️ Example updated state (priority: immediate)',
|
||||
'groupEnd: ⚛️ layout effects (priority: immediate)',
|
||||
'groupEnd: ⚛️ commit (priority: normal)',
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate experimental && enableDebugTracing
|
||||
it('should log render phase state updates for class component', () => {
|
||||
class Example extends React.Component {
|
||||
state = {didRender: false};
|
||||
render() {
|
||||
if (this.state.didRender === false) {
|
||||
this.setState({didRender: true});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
ReactTestRenderer.create(
|
||||
<React.unstable_DebugTracingMode>
|
||||
<Example />
|
||||
</React.unstable_DebugTracingMode>,
|
||||
{unstable_isConcurrent: true},
|
||||
);
|
||||
|
||||
expect(logs).toEqual([]);
|
||||
|
||||
logs.splice(0);
|
||||
|
||||
expect(() => {
|
||||
expect(Scheduler).toFlushUntilNextPaint([]);
|
||||
}).toErrorDev('Cannot update during an existing state transition');
|
||||
|
||||
expect(logs).toEqual([
|
||||
'group: ⚛️ render (priority: normal)',
|
||||
'log: ⚛️ Example updated state (priority: normal)',
|
||||
'log: ⚛️ Example updated state (priority: normal)',
|
||||
'groupEnd: ⚛️ render (priority: normal)',
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate experimental && enableDebugTracing
|
||||
it('should log cascading layout updates', () => {
|
||||
function Example() {
|
||||
const [didMount, setDidMount] = React.useState(false);
|
||||
React.useLayoutEffect(() => {
|
||||
setDidMount(true);
|
||||
}, []);
|
||||
return didMount;
|
||||
}
|
||||
|
||||
ReactTestRenderer.create(
|
||||
<React.unstable_DebugTracingMode>
|
||||
<Example />
|
||||
</React.unstable_DebugTracingMode>,
|
||||
{unstable_isConcurrent: true},
|
||||
);
|
||||
|
||||
expect(logs).toEqual([]);
|
||||
|
||||
logs.splice(0);
|
||||
|
||||
expect(Scheduler).toFlushUntilNextPaint([]);
|
||||
|
||||
expect(logs).toEqual([
|
||||
'group: ⚛️ commit (priority: normal)',
|
||||
'group: ⚛️ layout effects (priority: immediate)',
|
||||
'log: ⚛️ Example updated state (priority: immediate)',
|
||||
'groupEnd: ⚛️ layout effects (priority: immediate)',
|
||||
'groupEnd: ⚛️ commit (priority: normal)',
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate experimental && enableDebugTracing
|
||||
it('should log cascading passive updates', () => {
|
||||
function Example() {
|
||||
const [didMount, setDidMount] = React.useState(false);
|
||||
React.useEffect(() => {
|
||||
setDidMount(true);
|
||||
}, []);
|
||||
return didMount;
|
||||
}
|
||||
|
||||
ReactTestRenderer.act(() => {
|
||||
ReactTestRenderer.create(
|
||||
<React.unstable_DebugTracingMode>
|
||||
<Example />
|
||||
</React.unstable_DebugTracingMode>,
|
||||
{unstable_isConcurrent: true},
|
||||
);
|
||||
});
|
||||
expect(logs).toEqual([
|
||||
'group: ⚛️ passive effects (priority: normal)',
|
||||
'log: ⚛️ Example updated state (priority: normal)',
|
||||
'groupEnd: ⚛️ passive effects (priority: normal)',
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate experimental && enableDebugTracing
|
||||
it('should log render phase updates', () => {
|
||||
function Example() {
|
||||
const [didRender, setDidRender] = React.useState(false);
|
||||
if (!didRender) {
|
||||
setDidRender(true);
|
||||
}
|
||||
return didRender;
|
||||
}
|
||||
|
||||
ReactTestRenderer.act(() => {
|
||||
ReactTestRenderer.create(
|
||||
<React.unstable_DebugTracingMode>
|
||||
<Example />
|
||||
</React.unstable_DebugTracingMode>,
|
||||
{unstable_isConcurrent: true},
|
||||
);
|
||||
});
|
||||
expect(logs).toEqual([
|
||||
'group: ⚛️ render (priority: normal)',
|
||||
'log: ⚛️ Example updated state (priority: normal)',
|
||||
'log: ⚛️ Example updated state (priority: normal)', // debugRenderPhaseSideEffectsForStrictMode
|
||||
'groupEnd: ⚛️ render (priority: normal)',
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate experimental && enableDebugTracing
|
||||
it('should log when user code logs', () => {
|
||||
function Example() {
|
||||
console.log('Hello from user code');
|
||||
return null;
|
||||
}
|
||||
|
||||
ReactTestRenderer.create(
|
||||
<React.unstable_DebugTracingMode>
|
||||
<Example />
|
||||
</React.unstable_DebugTracingMode>,
|
||||
{unstable_isConcurrent: true},
|
||||
);
|
||||
|
||||
expect(logs).toEqual([]);
|
||||
|
||||
logs.splice(0);
|
||||
|
||||
expect(Scheduler).toFlushUntilNextPaint([]);
|
||||
|
||||
expect(logs).toEqual([
|
||||
'group: ⚛️ render (priority: normal)',
|
||||
'log: Hello from user code',
|
||||
'groupEnd: ⚛️ render (priority: normal)',
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate experimental
|
||||
it('should not log anything outside of a unstable_DebugTracingMode subtree', () => {
|
||||
function ExampleThatCascades() {
|
||||
const [didMount, setDidMount] = React.useState(false);
|
||||
React.useLayoutEffect(() => {
|
||||
setDidMount(true);
|
||||
}, []);
|
||||
return didMount;
|
||||
}
|
||||
|
||||
const fakeSuspensPromise = new Promise(() => {});
|
||||
function ExampleThatSuspends() {
|
||||
throw fakeSuspensPromise;
|
||||
}
|
||||
|
||||
function Example() {
|
||||
return null;
|
||||
}
|
||||
|
||||
ReactTestRenderer.create(
|
||||
<React.Fragment>
|
||||
<ExampleThatCascades />
|
||||
<React.Suspense fallback={null}>
|
||||
<ExampleThatSuspends />
|
||||
</React.Suspense>
|
||||
<React.unstable_DebugTracingMode>
|
||||
<Example />
|
||||
</React.unstable_DebugTracingMode>
|
||||
</React.Fragment>,
|
||||
);
|
||||
|
||||
expect(logs).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -51,5 +51,7 @@ export {
|
||||
// enableScopeAPI
|
||||
unstable_createScope,
|
||||
unstable_useOpaqueIdentifier,
|
||||
// enableDebugTracing
|
||||
unstable_DebugTracingMode,
|
||||
} from './src/React';
|
||||
export {jsx, jsxs, jsxDEV} from './src/jsx/ReactJSX';
|
||||
|
||||
@@ -46,4 +46,6 @@ export {
|
||||
// enableBlocksAPI
|
||||
block,
|
||||
unstable_useOpaqueIdentifier,
|
||||
// enableDebugTracing
|
||||
unstable_DebugTracingMode,
|
||||
} from './src/React';
|
||||
|
||||
@@ -59,6 +59,7 @@ export {
|
||||
createMutableSource,
|
||||
Fragment,
|
||||
Profiler,
|
||||
unstable_DebugTracingMode,
|
||||
StrictMode,
|
||||
Suspense,
|
||||
createElement,
|
||||
|
||||
@@ -50,5 +50,7 @@ export {
|
||||
// enableScopeAPI
|
||||
unstable_createScope,
|
||||
unstable_useOpaqueIdentifier,
|
||||
// enableDebugTracing
|
||||
unstable_DebugTracingMode,
|
||||
} from './src/React';
|
||||
export {jsx, jsxs, jsxDEV} from './src/jsx/ReactJSX';
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import ReactVersion from 'shared/ReactVersion';
|
||||
import {
|
||||
REACT_FRAGMENT_TYPE,
|
||||
REACT_DEBUG_TRACING_MODE_TYPE,
|
||||
REACT_PROFILER_TYPE,
|
||||
REACT_STRICT_MODE_TYPE,
|
||||
REACT_SUSPENSE_TYPE,
|
||||
@@ -96,6 +97,7 @@ export {
|
||||
REACT_FRAGMENT_TYPE as Fragment,
|
||||
REACT_PROFILER_TYPE as Profiler,
|
||||
REACT_STRICT_MODE_TYPE as StrictMode,
|
||||
REACT_DEBUG_TRACING_MODE_TYPE as unstable_DebugTracingMode,
|
||||
REACT_SUSPENSE_TYPE as Suspense,
|
||||
createElement,
|
||||
cloneElement,
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
// This prevents e.g. <img src=""> from making an unnecessar HTTP request for certain browsers.
|
||||
export const enableFilterEmptyStringAttributesDOM = false;
|
||||
|
||||
// Adds verbose console logging for e.g. state updates, suspense, and work loop stuff.
|
||||
// Intended to enable React core members to more easily debug scheduling issues in DEV builds.
|
||||
export const enableDebugTracing = false;
|
||||
|
||||
// Helps identify side effects in render-phase lifecycle hooks and setState
|
||||
// reducers by double invoking them in Strict Mode.
|
||||
export const debugRenderPhaseSideEffectsForStrictMode = __DEV__;
|
||||
|
||||
@@ -27,6 +27,7 @@ export let REACT_FUNDAMENTAL_TYPE = 0xead5;
|
||||
export let REACT_RESPONDER_TYPE = 0xead6;
|
||||
export let REACT_SCOPE_TYPE = 0xead7;
|
||||
export let REACT_OPAQUE_ID_TYPE = 0xeae0;
|
||||
export let REACT_DEBUG_TRACING_MODE_TYPE = 0xeae1;
|
||||
|
||||
if (typeof Symbol === 'function' && Symbol.for) {
|
||||
const symbolFor = Symbol.for;
|
||||
@@ -48,6 +49,7 @@ if (typeof Symbol === 'function' && Symbol.for) {
|
||||
REACT_RESPONDER_TYPE = symbolFor('react.responder');
|
||||
REACT_SCOPE_TYPE = symbolFor('react.scope');
|
||||
REACT_OPAQUE_ID_TYPE = symbolFor('react.opaque.id');
|
||||
REACT_DEBUG_TRACING_MODE_TYPE = symbolFor('react.debug_trace_mode');
|
||||
}
|
||||
|
||||
const MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
|
||||
|
||||
@@ -11,6 +11,7 @@ import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
|
||||
import typeof * as ExportsType from './ReactFeatureFlags.native-fb';
|
||||
|
||||
// The rest of the flags are static for better dead code elimination.
|
||||
export const enableDebugTracing = false;
|
||||
export const enableProfilerTimer = __PROFILE__;
|
||||
export const enableProfilerCommitHooks = false;
|
||||
export const enableSchedulerTracing = __PROFILE__;
|
||||
|
||||
@@ -11,6 +11,7 @@ import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
|
||||
import typeof * as ExportsType from './ReactFeatureFlags.native-oss';
|
||||
|
||||
export const debugRenderPhaseSideEffectsForStrictMode = false;
|
||||
export const enableDebugTracing = false;
|
||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
|
||||
export const warnAboutDeprecatedLifecycles = true;
|
||||
export const enableProfilerTimer = __PROFILE__;
|
||||
|
||||
@@ -11,6 +11,7 @@ import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
|
||||
import typeof * as ExportsType from './ReactFeatureFlags.test-renderer';
|
||||
|
||||
export const debugRenderPhaseSideEffectsForStrictMode = false;
|
||||
export const enableDebugTracing = false;
|
||||
export const warnAboutDeprecatedLifecycles = true;
|
||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
||||
export const enableProfilerTimer = __PROFILE__;
|
||||
|
||||
@@ -11,6 +11,7 @@ import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
|
||||
import typeof * as ExportsType from './ReactFeatureFlags.test-renderer.www';
|
||||
|
||||
export const debugRenderPhaseSideEffectsForStrictMode = false;
|
||||
export const enableDebugTracing = false;
|
||||
export const warnAboutDeprecatedLifecycles = true;
|
||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
||||
export const enableProfilerTimer = __PROFILE__;
|
||||
|
||||
@@ -11,6 +11,7 @@ import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
|
||||
import typeof * as ExportsType from './ReactFeatureFlags.testing';
|
||||
|
||||
export const debugRenderPhaseSideEffectsForStrictMode = false;
|
||||
export const enableDebugTracing = false;
|
||||
export const warnAboutDeprecatedLifecycles = true;
|
||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
||||
export const enableProfilerTimer = __PROFILE__;
|
||||
|
||||
@@ -11,6 +11,7 @@ import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
|
||||
import typeof * as ExportsType from './ReactFeatureFlags.testing.www';
|
||||
|
||||
export const debugRenderPhaseSideEffectsForStrictMode = false;
|
||||
export const enableDebugTracing = false;
|
||||
export const warnAboutDeprecatedLifecycles = true;
|
||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
||||
export const enableProfilerTimer = false;
|
||||
|
||||
@@ -20,6 +20,7 @@ export const disableInputAttributeSyncing = __VARIANT__;
|
||||
export const enableFilterEmptyStringAttributesDOM = __VARIANT__;
|
||||
export const enableModernEventSystem = __VARIANT__;
|
||||
export const enableLegacyFBSupport = __VARIANT__;
|
||||
export const enableDebugTracing = __VARIANT__;
|
||||
|
||||
// These are already tested in both modes using the build type dimension,
|
||||
// so we don't need to use __VARIANT__ to get extra coverage.
|
||||
|
||||
@@ -26,6 +26,7 @@ export const {
|
||||
enableModernEventSystem,
|
||||
enableFilterEmptyStringAttributesDOM,
|
||||
enableLegacyFBSupport,
|
||||
enableDebugTracing,
|
||||
} = dynamicFeatureFlags;
|
||||
|
||||
// On WWW, __EXPERIMENTAL__ is used for a new modern build.
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
REACT_FRAGMENT_TYPE,
|
||||
REACT_PROFILER_TYPE,
|
||||
REACT_PROVIDER_TYPE,
|
||||
REACT_DEBUG_TRACING_MODE_TYPE,
|
||||
REACT_STRICT_MODE_TYPE,
|
||||
REACT_SUSPENSE_TYPE,
|
||||
REACT_SUSPENSE_LIST_TYPE,
|
||||
@@ -32,6 +33,7 @@ export default function isValidElementType(type: mixed) {
|
||||
// Note: its typeof might be other than 'symbol' or 'number' if it's a polyfill.
|
||||
type === REACT_FRAGMENT_TYPE ||
|
||||
type === REACT_PROFILER_TYPE ||
|
||||
type === REACT_DEBUG_TRACING_MODE_TYPE ||
|
||||
type === REACT_STRICT_MODE_TYPE ||
|
||||
type === REACT_SUSPENSE_TYPE ||
|
||||
type === REACT_SUSPENSE_LIST_TYPE ||
|
||||
|
||||
Reference in New Issue
Block a user