mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Fix User Timing oddities with Suspense, pure, and lazy (#13833)
* Show pure components in fiber timings with name * Fix Suspense and lazy user timings * Tweak message and type name * Fix Flow
This commit is contained in:
+5
-1
@@ -20,6 +20,7 @@ import {
|
||||
ContextProvider,
|
||||
ContextConsumer,
|
||||
Mode,
|
||||
SuspenseComponent,
|
||||
} from 'shared/ReactWorkTags';
|
||||
|
||||
type MeasurementPhase =
|
||||
@@ -315,7 +316,10 @@ export function stopFailedWorkTimer(fiber: Fiber): void {
|
||||
return;
|
||||
}
|
||||
fiber._debugIsCurrentlyTiming = false;
|
||||
const warning = 'An error was thrown inside this error boundary';
|
||||
const warning =
|
||||
fiber.tag === SuspenseComponent
|
||||
? 'Rendering was suspended'
|
||||
: 'An error was thrown inside this error boundary';
|
||||
endFiberMark(fiber, null, warning);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -56,7 +56,7 @@ import ReactStrictModeWarnings from './ReactStrictModeWarnings';
|
||||
import warning from 'shared/warning';
|
||||
import warningWithoutStack from 'shared/warningWithoutStack';
|
||||
import * as ReactCurrentFiber from './ReactCurrentFiber';
|
||||
import {cancelWorkTimer} from './ReactDebugFiberPerf';
|
||||
import {startWorkTimer, cancelWorkTimer} from './ReactDebugFiberPerf';
|
||||
|
||||
import {
|
||||
mountChildFibers,
|
||||
@@ -720,11 +720,15 @@ function mountIndeterminateComponent(
|
||||
Component !== null &&
|
||||
typeof Component.then === 'function'
|
||||
) {
|
||||
// We can't start a User Timing measurement with correct label yet.
|
||||
// Cancel and resume right after we know the tag.
|
||||
cancelWorkTimer(workInProgress);
|
||||
Component = readLazyComponentType(Component);
|
||||
const resolvedTag = (workInProgress.tag = resolveLazyComponentTag(
|
||||
workInProgress,
|
||||
Component,
|
||||
));
|
||||
startWorkTimer(workInProgress);
|
||||
const resolvedProps = resolveDefaultProps(Component, props);
|
||||
let child;
|
||||
switch (resolvedTag) {
|
||||
|
||||
@@ -555,6 +555,58 @@ describe('ReactDebugFiberPerf', () => {
|
||||
expect(getFlameChart()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('supports pure', () => {
|
||||
const PureFoo = React.pure(function Foo() {
|
||||
return <div />;
|
||||
});
|
||||
ReactNoop.render(
|
||||
<Parent>
|
||||
<PureFoo />
|
||||
</Parent>,
|
||||
);
|
||||
ReactNoop.flush();
|
||||
expect(getFlameChart()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('supports Suspense and lazy', async () => {
|
||||
function Spinner() {
|
||||
return <span />;
|
||||
}
|
||||
|
||||
let resolve;
|
||||
const LazyFoo = React.lazy(
|
||||
() =>
|
||||
new Promise(r => {
|
||||
resolve = r;
|
||||
}),
|
||||
);
|
||||
|
||||
ReactNoop.render(
|
||||
<Parent>
|
||||
<React.unstable_Suspense fallback={<Spinner />}>
|
||||
<LazyFoo />
|
||||
</React.unstable_Suspense>
|
||||
</Parent>,
|
||||
);
|
||||
ReactNoop.flush();
|
||||
expect(getFlameChart()).toMatchSnapshot();
|
||||
|
||||
resolve(function Foo() {
|
||||
return <div />;
|
||||
});
|
||||
await LazyFoo;
|
||||
|
||||
ReactNoop.render(
|
||||
<Parent>
|
||||
<React.unstable_Suspense>
|
||||
<LazyFoo />
|
||||
</React.unstable_Suspense>
|
||||
</Parent>,
|
||||
);
|
||||
ReactNoop.flush();
|
||||
expect(getFlameChart()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('does not schedule an extra callback if setState is called during a synchronous commit phase', () => {
|
||||
class Component extends React.Component {
|
||||
state = {step: 1};
|
||||
|
||||
+48
@@ -362,6 +362,40 @@ exports[`ReactDebugFiberPerf skips parents during setState 1`] = `
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ReactDebugFiberPerf supports Suspense and lazy 1`] = `
|
||||
"⚛ (Waiting for async callback... will force flush in 5250 ms)
|
||||
|
||||
⚛ (React Tree Reconciliation: Completed Root)
|
||||
⚛ Parent [mount]
|
||||
⛔ Suspense [mount] Warning: Rendering was suspended
|
||||
⚛ Suspense [mount]
|
||||
⚛ Spinner [mount]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ReactDebugFiberPerf supports Suspense and lazy 2`] = `
|
||||
"⚛ (Waiting for async callback... will force flush in 5250 ms)
|
||||
|
||||
⚛ (React Tree Reconciliation: Completed Root)
|
||||
⚛ Parent [mount]
|
||||
⛔ Suspense [mount] Warning: Rendering was suspended
|
||||
⚛ Suspense [mount]
|
||||
⚛ Spinner [mount]
|
||||
|
||||
⚛ (Waiting for async callback... will force flush in 5250 ms)
|
||||
|
||||
⚛ (React Tree Reconciliation: Completed Root)
|
||||
⚛ Parent [mount]
|
||||
⚛ Suspense [mount]
|
||||
⚛ Foo [mount]
|
||||
|
||||
⚛ (Committing Changes)
|
||||
⚛ (Committing Snapshot Effects: 0 Total)
|
||||
⚛ (Committing Host Effects: 1 Total)
|
||||
⚛ (Calling Lifecycle Methods: 0 Total)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ReactDebugFiberPerf supports portals 1`] = `
|
||||
"⚛ (Waiting for async callback... will force flush in 5250 ms)
|
||||
|
||||
@@ -376,6 +410,20 @@ exports[`ReactDebugFiberPerf supports portals 1`] = `
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ReactDebugFiberPerf supports pure 1`] = `
|
||||
"⚛ (Waiting for async callback... will force flush in 5250 ms)
|
||||
|
||||
⚛ (React Tree Reconciliation: Completed Root)
|
||||
⚛ Parent [mount]
|
||||
⚛ Pure(Foo) [mount]
|
||||
|
||||
⚛ (Committing Changes)
|
||||
⚛ (Committing Snapshot Effects: 0 Total)
|
||||
⚛ (Committing Host Effects: 1 Total)
|
||||
⚛ (Calling Lifecycle Methods: 0 Total)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ReactDebugFiberPerf warns if an in-progress update is interrupted 1`] = `
|
||||
"⚛ (Waiting for async callback... will force flush in 5250 ms)
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
REACT_FORWARD_REF_TYPE,
|
||||
REACT_FRAGMENT_TYPE,
|
||||
REACT_PORTAL_TYPE,
|
||||
REACT_PURE_TYPE,
|
||||
REACT_PROFILER_TYPE,
|
||||
REACT_PROVIDER_TYPE,
|
||||
REACT_STRICT_MODE_TYPE,
|
||||
@@ -23,6 +24,18 @@ import {
|
||||
} from 'shared/ReactSymbols';
|
||||
import {refineResolvedThenable} from 'shared/ReactLazyComponent';
|
||||
|
||||
function getWrappedName(
|
||||
outerType: mixed,
|
||||
innerType: any,
|
||||
wrapperName: string,
|
||||
): string {
|
||||
const functionName = innerType.displayName || innerType.name || '';
|
||||
return (
|
||||
(outerType: any).displayName ||
|
||||
(functionName !== '' ? `${wrapperName}(${functionName})` : wrapperName)
|
||||
);
|
||||
}
|
||||
|
||||
function getComponentName(type: mixed): string | null {
|
||||
if (type == null) {
|
||||
// Host root, text node or just invalid type.
|
||||
@@ -64,12 +77,9 @@ function getComponentName(type: mixed): string | null {
|
||||
case REACT_PROVIDER_TYPE:
|
||||
return 'Context.Provider';
|
||||
case REACT_FORWARD_REF_TYPE:
|
||||
const renderFn = (type.render: any);
|
||||
const functionName = renderFn.displayName || renderFn.name || '';
|
||||
return (
|
||||
(type: any).displayName ||
|
||||
(functionName !== '' ? `ForwardRef(${functionName})` : 'ForwardRef')
|
||||
);
|
||||
return getWrappedName(type, type.render, 'ForwardRef');
|
||||
case REACT_PURE_TYPE:
|
||||
return getWrappedName(type, type.render, 'Pure');
|
||||
}
|
||||
if (typeof type.then === 'function') {
|
||||
const thenable: Thenable<mixed> = (type: any);
|
||||
|
||||
Reference in New Issue
Block a user