diff --git a/packages/react-devtools-shared/src/backend/DevToolsComponentStackFrame.js b/packages/react-devtools-shared/src/backend/DevToolsComponentStackFrame.js index 0e412a43f74c3..316245492c64f 100644 --- a/packages/react-devtools-shared/src/backend/DevToolsComponentStackFrame.js +++ b/packages/react-devtools-shared/src/backend/DevToolsComponentStackFrame.js @@ -33,10 +33,7 @@ import { import {disableLogs, reenableLogs} from './DevToolsConsolePatching'; let prefix; -export function describeBuiltInComponentFrame( - name: string, - ownerFn: void | null | Function, -): string { +export function describeBuiltInComponentFrame(name: string): string { if (prefix === undefined) { // Extract the VM specific prefix used by each line. try { @@ -51,10 +48,7 @@ export function describeBuiltInComponentFrame( } export function describeDebugInfoFrame(name: string, env: ?string): string { - return describeBuiltInComponentFrame( - name + (env ? ' (' + env + ')' : ''), - null, - ); + return describeBuiltInComponentFrame(name + (env ? ' (' + env + ')' : '')); } let reentry = false; @@ -292,7 +286,6 @@ export function describeNativeComponentFrame( export function describeClassComponentFrame( ctor: Function, - ownerFn: void | null | Function, currentDispatcherRef: CurrentDispatcherRef, ): string { return describeNativeComponentFrame(ctor, true, currentDispatcherRef); @@ -300,7 +293,6 @@ export function describeClassComponentFrame( export function describeFunctionComponentFrame( fn: Function, - ownerFn: void | null | Function, currentDispatcherRef: CurrentDispatcherRef, ): string { return describeNativeComponentFrame(fn, false, currentDispatcherRef); @@ -313,7 +305,6 @@ function shouldConstruct(Component: Function) { export function describeUnknownElementTypeFrameInDEV( type: any, - ownerFn: void | null | Function, currentDispatcherRef: CurrentDispatcherRef, ): string { if (!__DEV__) { @@ -330,15 +321,15 @@ export function describeUnknownElementTypeFrameInDEV( ); } if (typeof type === 'string') { - return describeBuiltInComponentFrame(type, ownerFn); + return describeBuiltInComponentFrame(type); } switch (type) { case SUSPENSE_NUMBER: case SUSPENSE_SYMBOL_STRING: - return describeBuiltInComponentFrame('Suspense', ownerFn); + return describeBuiltInComponentFrame('Suspense'); case SUSPENSE_LIST_NUMBER: case SUSPENSE_LIST_SYMBOL_STRING: - return describeBuiltInComponentFrame('SuspenseList', ownerFn); + return describeBuiltInComponentFrame('SuspenseList'); } if (typeof type === 'object') { switch (type.$$typeof) { @@ -346,7 +337,6 @@ export function describeUnknownElementTypeFrameInDEV( case FORWARD_REF_SYMBOL_STRING: return describeFunctionComponentFrame( type.render, - ownerFn, currentDispatcherRef, ); case MEMO_NUMBER: @@ -354,7 +344,6 @@ export function describeUnknownElementTypeFrameInDEV( // Memo may contain any component type so we recursively resolve it. return describeUnknownElementTypeFrameInDEV( type.type, - ownerFn, currentDispatcherRef, ); case LAZY_NUMBER: @@ -366,7 +355,6 @@ export function describeUnknownElementTypeFrameInDEV( // Lazy may contain any component type so we recursively resolve it. return describeUnknownElementTypeFrameInDEV( init(payload), - ownerFn, currentDispatcherRef, ); } catch (x) {} diff --git a/packages/react-devtools-shared/src/backend/DevToolsFiberComponentStack.js b/packages/react-devtools-shared/src/backend/DevToolsFiberComponentStack.js index 77f0adf37ea4e..6d99cf4f21e52 100644 --- a/packages/react-devtools-shared/src/backend/DevToolsFiberComponentStack.js +++ b/packages/react-devtools-shared/src/backend/DevToolsFiberComponentStack.js @@ -39,38 +39,30 @@ export function describeFiber( ClassComponent, } = workTagMap; - const owner: null | Function = __DEV__ - ? workInProgress._debugOwner - ? workInProgress._debugOwner.type - : null - : null; switch (workInProgress.tag) { case HostComponent: - return describeBuiltInComponentFrame(workInProgress.type, owner); + return describeBuiltInComponentFrame(workInProgress.type); case LazyComponent: - return describeBuiltInComponentFrame('Lazy', owner); + return describeBuiltInComponentFrame('Lazy'); case SuspenseComponent: - return describeBuiltInComponentFrame('Suspense', owner); + return describeBuiltInComponentFrame('Suspense'); case SuspenseListComponent: - return describeBuiltInComponentFrame('SuspenseList', owner); + return describeBuiltInComponentFrame('SuspenseList'); case FunctionComponent: case IndeterminateComponent: case SimpleMemoComponent: return describeFunctionComponentFrame( workInProgress.type, - owner, currentDispatcherRef, ); case ForwardRef: return describeFunctionComponentFrame( workInProgress.type.render, - owner, currentDispatcherRef, ); case ClassComponent: return describeClassComponentFrame( workInProgress.type, - owner, currentDispatcherRef, ); default: diff --git a/packages/react-devtools-shared/src/backend/renderer.js b/packages/react-devtools-shared/src/backend/renderer.js index 803ef4b76b549..1f333d1271d67 100644 --- a/packages/react-devtools-shared/src/backend/renderer.js +++ b/packages/react-devtools-shared/src/backend/renderer.js @@ -1952,15 +1952,24 @@ export function attach( const {key} = fiber; const displayName = getDisplayNameForFiber(fiber); const elementType = getElementTypeForFiber(fiber); - const {_debugOwner} = fiber; + const debugOwner = fiber._debugOwner; // Ideally we should call getFiberIDThrows() for _debugOwner, // since owners are almost always higher in the tree (and so have already been processed), // but in some (rare) instances reported in open source, a descendant mounts before an owner. // Since this is a DEV only field it's probably okay to also just lazily generate and ID here if needed. // See https://github.com/facebook/react/issues/21445 - const ownerID = - _debugOwner != null ? getOrGenerateFiberID(_debugOwner) : 0; + let ownerID: number; + if (debugOwner != null) { + if (typeof debugOwner.tag === 'number') { + ownerID = getOrGenerateFiberID((debugOwner: any)); + } else { + // TODO: Track Server Component Owners. + ownerID = 0; + } + } else { + ownerID = 0; + } const parentID = parentFiber ? getFiberIDThrows(parentFiber) : 0; const displayNameStringID = getStringID(displayName); @@ -3104,15 +3113,17 @@ export function attach( return null; } - const {_debugOwner} = fiber; - const owners: Array = [fiberToSerializedElement(fiber)]; - if (_debugOwner) { - let owner: null | Fiber = _debugOwner; - while (owner !== null) { - owners.unshift(fiberToSerializedElement(owner)); - owner = owner._debugOwner || null; + let owner = fiber._debugOwner; + while (owner != null) { + if (typeof owner.tag === 'number') { + const ownerFiber: Fiber = (owner: any); // Refined + owners.unshift(fiberToSerializedElement(ownerFiber)); + owner = ownerFiber._debugOwner; + } else { + // TODO: Track Server Component Owners. + break; } } @@ -3173,7 +3184,7 @@ export function attach( } const { - _debugOwner, + _debugOwner: debugOwner, stateNode, key, memoizedProps, @@ -3300,13 +3311,19 @@ export function attach( context = {value: context}; } - let owners = null; - if (_debugOwner) { - owners = ([]: Array); - let owner: null | Fiber = _debugOwner; - while (owner !== null) { - owners.push(fiberToSerializedElement(owner)); - owner = owner._debugOwner || null; + let owners: null | Array = null; + let owner = debugOwner; + while (owner != null) { + if (typeof owner.tag === 'number') { + const ownerFiber: Fiber = (owner: any); // Refined + if (owners === null) { + owners = []; + } + owners.push(fiberToSerializedElement(ownerFiber)); + owner = ownerFiber._debugOwner; + } else { + // TODO: Track Server Component Owners. + break; } } diff --git a/packages/react-native-renderer/src/ReactNativeFiberInspector.js b/packages/react-native-renderer/src/ReactNativeFiberInspector.js index f30012b2cf917..14d87f7a5502b 100644 --- a/packages/react-native-renderer/src/ReactNativeFiberInspector.js +++ b/packages/react-native-renderer/src/ReactNativeFiberInspector.js @@ -103,13 +103,21 @@ function getInspectorDataForInstance( } const fiber = findCurrentFiberUsingSlowPath(closestInstance); + if (fiber === null) { + // Might not be currently mounted. + return { + hierarchy: [], + props: emptyObject, + selectedIndex: null, + componentStack: '', + }; + } const fiberHierarchy = getOwnerHierarchy(fiber); const instance = lastNonHostInstance(fiberHierarchy); const hierarchy = createHierarchy(fiberHierarchy); const props = getHostProps(instance); const selectedIndex = fiberHierarchy.indexOf(instance); - const componentStack = - fiber !== null ? getStackByFiberInDevAndProd(fiber) : ''; + const componentStack = getStackByFiberInDevAndProd(fiber); return { closestInstance: instance, @@ -125,7 +133,7 @@ function getInspectorDataForInstance( ); } -function getOwnerHierarchy(instance: any) { +function getOwnerHierarchy(instance: Fiber) { const hierarchy: Array<$FlowFixMe> = []; traverseOwnerTreeUp(hierarchy, instance); return hierarchy; @@ -143,15 +151,17 @@ function lastNonHostInstance(hierarchy) { return hierarchy[0]; } -// $FlowFixMe[missing-local-annot] function traverseOwnerTreeUp( hierarchy: Array<$FlowFixMe>, - instance: any, + instance: Fiber, ): void { if (__DEV__ || enableGetInspectorDataForInstanceInProduction) { - if (instance) { - hierarchy.unshift(instance); - traverseOwnerTreeUp(hierarchy, instance._debugOwner); + hierarchy.unshift(instance); + const owner = instance._debugOwner; + if (owner != null && typeof owner.tag === 'number') { + traverseOwnerTreeUp(hierarchy, (owner: any)); + } else { + // TODO: Traverse Server Components owners. } } } diff --git a/packages/react-reconciler/src/ReactCurrentFiber.js b/packages/react-reconciler/src/ReactCurrentFiber.js index 6c283ed0d35f4..c52d55c0cce73 100644 --- a/packages/react-reconciler/src/ReactCurrentFiber.js +++ b/packages/react-reconciler/src/ReactCurrentFiber.js @@ -11,7 +11,7 @@ import type {Fiber} from './ReactInternalTypes'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import {getStackByFiberInDevAndProd} from './ReactFiberComponentStack'; -import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber'; +import {getComponentNameFromOwner} from 'react-reconciler/src/getComponentNameFromFiber'; const ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; @@ -24,8 +24,8 @@ export function getCurrentFiberOwnerNameInDevOrNull(): string | null { return null; } const owner = current._debugOwner; - if (owner !== null && typeof owner !== 'undefined') { - return getComponentNameFromFiber(owner); + if (owner != null) { + return getComponentNameFromOwner(owner); } } return null; diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js index 7b1a6514ebd5f..1de7d42cfe7d0 100644 --- a/packages/react-reconciler/src/ReactFiber.js +++ b/packages/react-reconciler/src/ReactFiber.js @@ -68,7 +68,7 @@ import { TracingMarkerComponent, } from './ReactWorkTags'; import {OffscreenVisible} from './ReactFiberActivityComponent'; -import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber'; +import {getComponentNameFromOwner} from 'react-reconciler/src/getComponentNameFromFiber'; import {isDevToolsPresent} from './ReactFiberDevToolsHook'; import { resolveClassForHotReloading, @@ -110,6 +110,7 @@ import { attachOffscreenInstance, } from './ReactFiberCommitWork'; import {getHostContext} from './ReactFiberHostContext'; +import type {ReactComponentInfo} from '../../shared/ReactTypes'; export type {Fiber}; @@ -475,7 +476,7 @@ export function createFiberFromTypeAndProps( type: any, // React$ElementType key: null | string, pendingProps: any, - owner: null | Fiber, + owner: null | ReactComponentInfo | Fiber, mode: TypeOfMode, lanes: Lanes, ): Fiber { @@ -610,7 +611,7 @@ export function createFiberFromTypeAndProps( "it's defined in, or you might have mixed up default and " + 'named imports.'; } - const ownerName = owner ? getComponentNameFromFiber(owner) : null; + const ownerName = owner ? getComponentNameFromOwner(owner) : null; if (ownerName) { info += '\n\nCheck the render method of `' + ownerName + '`.'; } diff --git a/packages/react-reconciler/src/ReactFiberComponentStack.js b/packages/react-reconciler/src/ReactFiberComponentStack.js index f292cb51d10b4..193c27aef1e1a 100644 --- a/packages/react-reconciler/src/ReactFiberComponentStack.js +++ b/packages/react-reconciler/src/ReactFiberComponentStack.js @@ -29,29 +29,24 @@ import { } from 'shared/ReactComponentStackFrame'; function describeFiber(fiber: Fiber): string { - const owner: null | Function = __DEV__ - ? fiber._debugOwner - ? fiber._debugOwner.type - : null - : null; switch (fiber.tag) { case HostHoistable: case HostSingleton: case HostComponent: - return describeBuiltInComponentFrame(fiber.type, owner); + return describeBuiltInComponentFrame(fiber.type); case LazyComponent: - return describeBuiltInComponentFrame('Lazy', owner); + return describeBuiltInComponentFrame('Lazy'); case SuspenseComponent: - return describeBuiltInComponentFrame('Suspense', owner); + return describeBuiltInComponentFrame('Suspense'); case SuspenseListComponent: - return describeBuiltInComponentFrame('SuspenseList', owner); + return describeBuiltInComponentFrame('SuspenseList'); case FunctionComponent: case SimpleMemoComponent: - return describeFunctionComponentFrame(fiber.type, owner); + return describeFunctionComponentFrame(fiber.type); case ForwardRef: - return describeFunctionComponentFrame(fiber.type.render, owner); + return describeFunctionComponentFrame(fiber.type.render); case ClassComponent: - return describeClassComponentFrame(fiber.type, owner); + return describeClassComponentFrame(fiber.type); default: return ''; } diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 3b82dc40c828b..f12b9a16c570b 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -15,6 +15,7 @@ import type { Usable, ReactFormState, Awaited, + ReactComponentInfo, ReactDebugInfo, } from 'shared/ReactTypes'; import type {WorkTag} from './ReactWorkTags'; @@ -193,7 +194,7 @@ export type Fiber = { // __DEV__ only _debugInfo?: ReactDebugInfo | null, - _debugOwner?: Fiber | null, + _debugOwner?: ReactComponentInfo | Fiber | null, _debugIsCurrentlyTiming?: boolean, _debugNeedsRemount?: boolean, diff --git a/packages/react-reconciler/src/getComponentNameFromFiber.js b/packages/react-reconciler/src/getComponentNameFromFiber.js index 6659b14b7f0d0..332324900fa88 100644 --- a/packages/react-reconciler/src/getComponentNameFromFiber.js +++ b/packages/react-reconciler/src/getComponentNameFromFiber.js @@ -47,6 +47,7 @@ import { } from 'react-reconciler/src/ReactWorkTags'; import getComponentNameFromType from 'shared/getComponentNameFromType'; import {REACT_STRICT_MODE_TYPE} from 'shared/ReactSymbols'; +import type {ReactComponentInfo} from '../../shared/ReactTypes'; // Keep in sync with shared/getComponentNameFromType function getWrappedName( @@ -66,6 +67,18 @@ function getContextName(type: ReactContext) { return type.displayName || 'Context'; } +export function getComponentNameFromOwner( + owner: Fiber | ReactComponentInfo, +): string | null { + if (typeof owner.tag === 'number') { + return getComponentNameFromFiber((owner: any)); + } + if (typeof owner.name === 'string') { + return owner.name; + } + return null; +} + export default function getComponentNameFromFiber(fiber: Fiber): string | null { const {tag, type} = fiber; switch (tag) { diff --git a/packages/react-server/src/ReactFizzComponentStack.js b/packages/react-server/src/ReactFizzComponentStack.js index 7aeb6b0802c92..84b4d82d8d45f 100644 --- a/packages/react-server/src/ReactFizzComponentStack.js +++ b/packages/react-server/src/ReactFizzComponentStack.js @@ -43,13 +43,13 @@ export function getStackByComponentStackNode( do { switch (node.tag) { case 0: - info += describeBuiltInComponentFrame(node.type, null); + info += describeBuiltInComponentFrame(node.type); break; case 1: - info += describeFunctionComponentFrame(node.type, null); + info += describeFunctionComponentFrame(node.type); break; case 2: - info += describeClassComponentFrame(node.type, null); + info += describeClassComponentFrame(node.type); break; } // $FlowFixMe[incompatible-type] we bail out when we get a null diff --git a/packages/react/src/jsx/ReactJSXElement.js b/packages/react/src/jsx/ReactJSXElement.js index d20c1d4a25b56..0b8fb2f149c1e 100644 --- a/packages/react/src/jsx/ReactJSXElement.js +++ b/packages/react/src/jsx/ReactJSXElement.js @@ -1008,13 +1008,17 @@ function validateExplicitKey(element, parentType) { let childOwner = ''; if ( element && - element._owner && + element._owner != null && element._owner !== ReactCurrentOwner.current ) { + let ownerName = null; + if (typeof element._owner.tag === 'number') { + ownerName = getComponentNameFromType(element._owner.type); + } else if (typeof element._owner.name === 'string') { + ownerName = element._owner.name; + } // Give the component that originally created this child. - childOwner = ` It was passed a child from ${getComponentNameFromType( - element._owner.type, - )}.`; + childOwner = ` It was passed a child from ${ownerName}.`; } setCurrentlyValidatingElement(element); diff --git a/packages/shared/ReactComponentStackFrame.js b/packages/shared/ReactComponentStackFrame.js index c928f191d7b79..b103b760e9954 100644 --- a/packages/shared/ReactComponentStackFrame.js +++ b/packages/shared/ReactComponentStackFrame.js @@ -26,10 +26,7 @@ import ReactSharedInternals from 'shared/ReactSharedInternals'; const {ReactCurrentDispatcher} = ReactSharedInternals; let prefix; -export function describeBuiltInComponentFrame( - name: string, - ownerFn: void | null | Function, -): string { +export function describeBuiltInComponentFrame(name: string): string { if (enableComponentStackLocations) { if (prefix === undefined) { // Extract the VM specific prefix used by each line. @@ -43,19 +40,12 @@ export function describeBuiltInComponentFrame( // We use the prefix to ensure our stacks line up with native stack frames. return '\n' + prefix + name; } else { - let ownerName = null; - if (__DEV__ && ownerFn) { - ownerName = ownerFn.displayName || ownerFn.name || null; - } - return describeComponentFrame(name, ownerName); + return describeComponentFrame(name); } } export function describeDebugInfoFrame(name: string, env: ?string): string { - return describeBuiltInComponentFrame( - name + (env ? ' (' + env + ')' : ''), - null, - ); + return describeBuiltInComponentFrame(name + (env ? ' (' + env + ')' : '')); } let reentry = false; @@ -298,29 +288,19 @@ export function describeNativeComponentFrame( return syntheticFrame; } -function describeComponentFrame(name: null | string, ownerName: null | string) { - let sourceInfo = ''; - if (ownerName) { - sourceInfo = ' (created by ' + ownerName + ')'; - } - return '\n in ' + (name || 'Unknown') + sourceInfo; +function describeComponentFrame(name: null | string) { + return '\n in ' + (name || 'Unknown'); } -export function describeClassComponentFrame( - ctor: Function, - ownerFn: void | null | Function, -): string { +export function describeClassComponentFrame(ctor: Function): string { if (enableComponentStackLocations) { return describeNativeComponentFrame(ctor, true); } else { - return describeFunctionComponentFrame(ctor, ownerFn); + return describeFunctionComponentFrame(ctor); } } -export function describeFunctionComponentFrame( - fn: Function, - ownerFn: void | null | Function, -): string { +export function describeFunctionComponentFrame(fn: Function): string { if (enableComponentStackLocations) { return describeNativeComponentFrame(fn, false); } else { @@ -328,11 +308,7 @@ export function describeFunctionComponentFrame( return ''; } const name = fn.displayName || fn.name || null; - let ownerName = null; - if (__DEV__ && ownerFn) { - ownerName = ownerFn.displayName || ownerFn.name || null; - } - return describeComponentFrame(name, ownerName); + return describeComponentFrame(name); } } @@ -341,10 +317,7 @@ function shouldConstruct(Component: Function) { return !!(prototype && prototype.isReactComponent); } -export function describeUnknownElementTypeFrameInDEV( - type: any, - ownerFn: void | null | Function, -): string { +export function describeUnknownElementTypeFrameInDEV(type: any): string { if (!__DEV__) { return ''; } @@ -355,32 +328,32 @@ export function describeUnknownElementTypeFrameInDEV( if (enableComponentStackLocations) { return describeNativeComponentFrame(type, shouldConstruct(type)); } else { - return describeFunctionComponentFrame(type, ownerFn); + return describeFunctionComponentFrame(type); } } if (typeof type === 'string') { - return describeBuiltInComponentFrame(type, ownerFn); + return describeBuiltInComponentFrame(type); } switch (type) { case REACT_SUSPENSE_TYPE: - return describeBuiltInComponentFrame('Suspense', ownerFn); + return describeBuiltInComponentFrame('Suspense'); case REACT_SUSPENSE_LIST_TYPE: - return describeBuiltInComponentFrame('SuspenseList', ownerFn); + return describeBuiltInComponentFrame('SuspenseList'); } if (typeof type === 'object') { switch (type.$$typeof) { case REACT_FORWARD_REF_TYPE: - return describeFunctionComponentFrame(type.render, ownerFn); + return describeFunctionComponentFrame(type.render); case REACT_MEMO_TYPE: // Memo may contain any component type so we recursively resolve it. - return describeUnknownElementTypeFrameInDEV(type.type, ownerFn); + return describeUnknownElementTypeFrameInDEV(type.type); case REACT_LAZY_TYPE: { const lazyComponent: LazyComponent = (type: any); const payload = lazyComponent._payload; const init = lazyComponent._init; try { // Lazy may contain any component type so we recursively resolve it. - return describeUnknownElementTypeFrameInDEV(init(payload), ownerFn); + return describeUnknownElementTypeFrameInDEV(init(payload)); } catch (x) {} } }