diff --git a/packages/react-devtools-shared/src/devtools/views/useInferredName.js b/packages/react-devtools-shared/src/devtools/views/useInferredName.js index f0822e126d7..2c93ded4691 100644 --- a/packages/react-devtools-shared/src/devtools/views/useInferredName.js +++ b/packages/react-devtools-shared/src/devtools/views/useInferredName.js @@ -16,7 +16,7 @@ export default function useInferredName( const fetchFileWithCaching = useContext(FetchFileWithCachingContext); const name = asyncInfo.awaited.name; let inferNameFromStack = null; - if (!name || name === 'Promise') { + if (!name || name === 'Promise' || name === 'lazy') { // If all we have is a generic name, we can try to infer a better name from // the stack. We only do this if the stack has more than one frame since // otherwise it's likely to just be the name of the component which isn't better. diff --git a/packages/react/src/ReactLazy.js b/packages/react/src/ReactLazy.js index 55b1690b7ca..47f37afbae2 100644 --- a/packages/react/src/ReactLazy.js +++ b/packages/react/src/ReactLazy.js @@ -20,6 +20,8 @@ import {enableAsyncDebugInfo} from 'shared/ReactFeatureFlags'; import {REACT_LAZY_TYPE} from 'shared/ReactSymbols'; +import noop from 'shared/noop'; + const Uninitialized = -1; const Pending = 0; const Resolved = 1; @@ -67,12 +69,20 @@ export type LazyComponent = { function lazyInitializer(payload: Payload): T { if (payload._status === Uninitialized) { + let resolveDebugValue: (void | T) => void = (null: any); + let rejectDebugValue: mixed => void = (null: any); if (__DEV__ && enableAsyncDebugInfo) { const ioInfo = payload._ioInfo; if (ioInfo != null) { // Mark when we first kicked off the lazy request. // $FlowFixMe[cannot-write] ioInfo.start = ioInfo.end = performance.now(); + // Stash a Promise for introspection of the value later. + // $FlowFixMe[cannot-write] + ioInfo.value = new Promise((resolve, reject) => { + resolveDebugValue = resolve; + rejectDebugValue = reject; + }); } } const ctor = payload._result; @@ -92,12 +102,20 @@ function lazyInitializer(payload: Payload): T { const resolved: ResolvedPayload = (payload: any); resolved._status = Resolved; resolved._result = moduleObject; - if (__DEV__) { + if (__DEV__ && enableAsyncDebugInfo) { const ioInfo = payload._ioInfo; if (ioInfo != null) { // Mark the end time of when we resolved. // $FlowFixMe[cannot-write] ioInfo.end = performance.now(); + // Surface the default export as the resolved "value" for debug purposes. + const debugValue = + moduleObject == null ? undefined : moduleObject.default; + resolveDebugValue(debugValue); + // $FlowFixMe + ioInfo.value.status = 'fulfilled'; + // $FlowFixMe + ioInfo.value.value = debugValue; } // Make the thenable introspectable if (thenable.status === undefined) { @@ -124,6 +142,14 @@ function lazyInitializer(payload: Payload): T { // Mark the end time of when we rejected. // $FlowFixMe[cannot-write] ioInfo.end = performance.now(); + // Hide unhandled rejections. + // $FlowFixMe + ioInfo.value.then(noop, noop); + rejectDebugValue(error); + // $FlowFixMe + ioInfo.value.status = 'rejected'; + // $FlowFixMe + ioInfo.value.reason = error; } // Make the thenable introspectable if (thenable.status === undefined) { @@ -139,9 +165,6 @@ function lazyInitializer(payload: Payload): T { if (__DEV__ && enableAsyncDebugInfo) { const ioInfo = payload._ioInfo; if (ioInfo != null) { - // Stash the thenable for introspection of the value later. - // $FlowFixMe[cannot-write] - ioInfo.value = thenable; const displayName = thenable.displayName; if (typeof displayName === 'string') { // $FlowFixMe[cannot-write] diff --git a/packages/shared/ReactIODescription.js b/packages/shared/ReactIODescription.js index e1a0fce2c4d..6d7bf648fc8 100644 --- a/packages/shared/ReactIODescription.js +++ b/packages/shared/ReactIODescription.js @@ -13,6 +13,8 @@ export function getIODescription(value: mixed): string { } try { switch (typeof value) { + case 'function': + return value.name || ''; case 'object': // Test the object for a bunch of common property names that are useful identifiers. // While we only have the return value here, it should ideally be a name that