diff --git a/packages/react-devtools-shared/src/backend/renderer.js b/packages/react-devtools-shared/src/backend/renderer.js index b1e5dac527e61..803ef4b76b549 100644 --- a/packages/react-devtools-shared/src/backend/renderer.js +++ b/packages/react-devtools-shared/src/backend/renderer.js @@ -225,7 +225,8 @@ export function getInternalReactConstants(version: string): { HostSingleton: 27, // Same as above HostText: 6, IncompleteClassComponent: 17, - IndeterminateComponent: 2, + IncompleteFunctionComponent: 28, + IndeterminateComponent: 2, // removed in 19.0.0 LazyComponent: 16, LegacyHiddenComponent: 23, MemoComponent: 14, @@ -259,6 +260,7 @@ export function getInternalReactConstants(version: string): { HostSingleton: -1, // Doesn't exist yet HostText: 6, IncompleteClassComponent: 17, + IncompleteFunctionComponent: -1, // Doesn't exist yet IndeterminateComponent: 2, LazyComponent: 16, LegacyHiddenComponent: 24, @@ -292,6 +294,7 @@ export function getInternalReactConstants(version: string): { HostSingleton: -1, // Doesn't exist yet HostText: 6, IncompleteClassComponent: 17, + IncompleteFunctionComponent: -1, // Doesn't exist yet IndeterminateComponent: 2, LazyComponent: 16, LegacyHiddenComponent: -1, @@ -325,6 +328,7 @@ export function getInternalReactConstants(version: string): { HostSingleton: -1, // Doesn't exist yet HostText: 8, IncompleteClassComponent: -1, // Doesn't exist yet + IncompleteFunctionComponent: -1, // Doesn't exist yet IndeterminateComponent: 4, LazyComponent: -1, // Doesn't exist yet LegacyHiddenComponent: -1, @@ -358,6 +362,7 @@ export function getInternalReactConstants(version: string): { HostSingleton: -1, // Doesn't exist yet HostText: 6, IncompleteClassComponent: -1, // Doesn't exist yet + IncompleteFunctionComponent: -1, // Doesn't exist yet IndeterminateComponent: 0, LazyComponent: -1, // Doesn't exist yet LegacyHiddenComponent: -1, @@ -391,6 +396,7 @@ export function getInternalReactConstants(version: string): { CacheComponent, ClassComponent, IncompleteClassComponent, + IncompleteFunctionComponent, FunctionComponent, IndeterminateComponent, ForwardRef, @@ -459,6 +465,7 @@ export function getInternalReactConstants(version: string): { return 'Cache'; case ClassComponent: case IncompleteClassComponent: + case IncompleteFunctionComponent: case FunctionComponent: case IndeterminateComponent: return getDisplayName(resolvedType); @@ -624,6 +631,7 @@ export function attach( HostComponent, HostText, IncompleteClassComponent, + IncompleteFunctionComponent, IndeterminateComponent, LegacyHiddenComponent, MemoComponent, @@ -1061,6 +1069,7 @@ export function attach( case ClassComponent: case IncompleteClassComponent: return ElementTypeClass; + case IncompleteFunctionComponent: case FunctionComponent: case IndeterminateComponent: return ElementTypeFunction; @@ -3059,6 +3068,7 @@ export function attach( switch (tag) { case ClassComponent: case IncompleteClassComponent: + case IncompleteFunctionComponent: case IndeterminateComponent: case FunctionComponent: global.$type = type; @@ -3193,6 +3203,7 @@ export function attach( tag === ClassComponent || tag === FunctionComponent || tag === IncompleteClassComponent || + tag === IncompleteFunctionComponent || tag === IndeterminateComponent || tag === MemoComponent || tag === ForwardRef || @@ -3540,6 +3551,7 @@ export function attach( case IndeterminateComponent: global.$r = stateNode; break; + case IncompleteFunctionComponent: case FunctionComponent: global.$r = { hooks, diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js index df45122f6314f..c006cf53e3d5b 100644 --- a/packages/react-devtools-shared/src/backend/types.js +++ b/packages/react-devtools-shared/src/backend/types.js @@ -58,6 +58,7 @@ export type WorkTagMap = { HostSingleton: WorkTag, HostText: WorkTag, IncompleteClassComponent: WorkTag, + IncompleteFunctionComponent: WorkTag, IndeterminateComponent: WorkTag, LazyComponent: WorkTag, LegacyHiddenComponent: WorkTag, diff --git a/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js b/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js index 5959cdefccfe3..2e56a911a0c38 100644 --- a/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js @@ -223,23 +223,16 @@ describe('ReactCompositeComponent', () => { const el = document.createElement('div'); const root = ReactDOMClient.createRoot(el); await expect(async () => { - await expect(async () => { - await act(() => { - root.render(); - }); - }).rejects.toThrow( - 'Objects are not valid as a React child (found: object with keys {render}).', - ); - }).toErrorDev( - 'Warning: The component appears to be a function component that returns a class instance. ' + - 'Change Child to a class that extends React.Component instead. ' + - "If you can't use a class try assigning the prototype on the function as a workaround. " + - '`Child.prototype = React.Component.prototype`. ' + - "Don't use an arrow function since it cannot be called with `new` by React.", + await act(() => { + root.render(); + }); + }).rejects.toThrow( + 'Objects are not valid as a React child (found: object with keys {render}).', ); expect(el.textContent).toBe(''); }); + it('should use default values for undefined props', async () => { class Component extends React.Component { static defaultProps = {prop: 'testKey'}; diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js index 5b8dc06fb5f69..d676966907826 100644 --- a/packages/react-reconciler/src/ReactFiber.js +++ b/packages/react-reconciler/src/ReactFiber.js @@ -41,7 +41,6 @@ import { import {NoFlags, Placement, StaticMask} from './ReactFiberFlags'; import {ConcurrentRoot} from './ReactRootTags'; import { - IndeterminateComponent, ClassComponent, HostRoot, HostComponent, @@ -245,19 +244,10 @@ export function isSimpleFunctionComponent(type: any): boolean { ); } -export function resolveLazyComponentTag(Component: Function): WorkTag { - if (typeof Component === 'function') { - return shouldConstruct(Component) ? ClassComponent : FunctionComponent; - } else if (Component !== undefined && Component !== null) { - const $$typeof = Component.$$typeof; - if ($$typeof === REACT_FORWARD_REF_TYPE) { - return ForwardRef; - } - if ($$typeof === REACT_MEMO_TYPE) { - return MemoComponent; - } - } - return IndeterminateComponent; +export function isFunctionClassComponent( + type: (...args: Array) => mixed, +): boolean { + return shouldConstruct(type); } // This is used to create an alternate fiber to do work on. @@ -348,7 +338,6 @@ export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber { workInProgress._debugInfo = current._debugInfo; workInProgress._debugNeedsRemount = current._debugNeedsRemount; switch (workInProgress.tag) { - case IndeterminateComponent: case FunctionComponent: case SimpleMemoComponent: workInProgress.type = resolveFunctionForHotReloading(current.type); @@ -489,7 +478,7 @@ export function createFiberFromTypeAndProps( mode: TypeOfMode, lanes: Lanes, ): Fiber { - let fiberTag = IndeterminateComponent; + let fiberTag = FunctionComponent; // The resolved type is set if we know what the final type will be. I.e. it's not lazy. let resolvedType = type; if (typeof type === 'function') { diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index 9e745a69c5c83..fbf7988cdea78 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -46,7 +46,6 @@ import { setIsStrictModeForDevtools, } from './ReactFiberDevToolsHook'; import { - IndeterminateComponent, FunctionComponent, ClassComponent, HostRoot, @@ -67,6 +66,7 @@ import { SimpleMemoComponent, LazyComponent, IncompleteClassComponent, + IncompleteFunctionComponent, ScopeComponent, OffscreenComponent, LegacyHiddenComponent, @@ -114,7 +114,12 @@ import shallowEqual from 'shared/shallowEqual'; import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber'; import getComponentNameFromType from 'shared/getComponentNameFromType'; import ReactStrictModeWarnings from './ReactStrictModeWarnings'; -import {REACT_LAZY_TYPE, getIteratorFn} from 'shared/ReactSymbols'; +import { + REACT_LAZY_TYPE, + REACT_FORWARD_REF_TYPE, + REACT_MEMO_TYPE, + getIteratorFn, +} from 'shared/ReactSymbols'; import { getCurrentFiberOwnerNameInDevOrNull, setIsRendering, @@ -242,12 +247,12 @@ import { } from './ReactFiberClassComponent'; import {resolveDefaultProps} from './ReactFiberLazyComponent'; import { - resolveLazyComponentTag, createFiberFromTypeAndProps, createFiberFromFragment, createFiberFromOffscreen, createWorkInProgress, isSimpleFunctionComponent, + isFunctionClassComponent, } from './ReactFiber'; import { retryDehydratedSuspenseBoundary, @@ -303,7 +308,6 @@ export const SelectiveHydrationException: mixed = new Error( let didReceiveUpdate: boolean = false; let didWarnAboutBadClass; -let didWarnAboutModulePatternComponent; let didWarnAboutContextTypeOnFunctionComponent; let didWarnAboutGetDerivedStateOnFunctionComponent; let didWarnAboutFunctionRefs; @@ -314,7 +318,6 @@ let didWarnAboutDefaultPropsOnFunctionComponent; if (__DEV__) { didWarnAboutBadClass = ({}: {[string]: boolean}); - didWarnAboutModulePatternComponent = ({}: {[string]: boolean}); didWarnAboutContextTypeOnFunctionComponent = ({}: {[string]: boolean}); didWarnAboutGetDerivedStateOnFunctionComponent = ({}: {[string]: boolean}); didWarnAboutFunctionRefs = ({}: {[string]: boolean}); @@ -1044,6 +1047,26 @@ function markRef(current: Fiber | null, workInProgress: Fiber) { } } +function mountIncompleteFunctionComponent( + _current: null | Fiber, + workInProgress: Fiber, + Component: any, + nextProps: any, + renderLanes: Lanes, +) { + resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress); + + workInProgress.tag = FunctionComponent; + + return updateFunctionComponent( + null, + workInProgress, + Component, + nextProps, + renderLanes, + ); +} + function updateFunctionComponent( current: null | Fiber, workInProgress: Fiber, @@ -1051,6 +1074,43 @@ function updateFunctionComponent( nextProps: any, renderLanes: Lanes, ) { + if (__DEV__) { + if ( + Component.prototype && + typeof Component.prototype.render === 'function' + ) { + const componentName = getComponentNameFromType(Component) || 'Unknown'; + + if (!didWarnAboutBadClass[componentName]) { + console.error( + "The <%s /> component appears to have a render method, but doesn't extend React.Component. " + + 'This is likely to cause errors. Change %s to extend React.Component instead.', + componentName, + componentName, + ); + didWarnAboutBadClass[componentName] = true; + } + } + + if (workInProgress.mode & StrictLegacyMode) { + ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null); + } + + if (current === null) { + // Some validations were previously done in mountIndeterminateComponent however and are now run + // in updateFuntionComponent but only on mount + validateFunctionComponentInDev(workInProgress, workInProgress.type); + + if (disableLegacyContext && Component.contextTypes) { + console.error( + '%s uses the legacy contextTypes API which was removed in React 19. ' + + 'Use React.createContext() with React.useContext() instead.', + getComponentNameFromType(Component) || 'Unknown', + ); + } + } + } + let context; if (!disableLegacyContext) { const unmaskedContext = getUnmaskedContext(workInProgress, Component, true); @@ -1697,64 +1757,64 @@ function mountLazyComponent( let Component = init(payload); // Store the unwrapped component in the type. workInProgress.type = Component; - const resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component)); + const resolvedProps = resolveDefaultProps(Component, props); - let child; - switch (resolvedTag) { - case FunctionComponent: { + if (typeof Component === 'function') { + if (isFunctionClassComponent(Component)) { + workInProgress.tag = ClassComponent; if (__DEV__) { - validateFunctionComponentInDev(workInProgress, Component); workInProgress.type = Component = - resolveFunctionForHotReloading(Component); + resolveClassForHotReloading(Component); } - child = updateFunctionComponent( + return updateClassComponent( null, workInProgress, Component, resolvedProps, renderLanes, ); - return child; - } - case ClassComponent: { + } else { + workInProgress.tag = FunctionComponent; if (__DEV__) { + validateFunctionComponentInDev(workInProgress, Component); workInProgress.type = Component = - resolveClassForHotReloading(Component); + resolveFunctionForHotReloading(Component); } - child = updateClassComponent( + return updateFunctionComponent( null, workInProgress, Component, resolvedProps, renderLanes, ); - return child; } - case ForwardRef: { + } else if (Component !== undefined && Component !== null) { + const $$typeof = Component.$$typeof; + if ($$typeof === REACT_FORWARD_REF_TYPE) { + workInProgress.tag = ForwardRef; if (__DEV__) { workInProgress.type = Component = resolveForwardRefForHotReloading(Component); } - child = updateForwardRef( + return updateForwardRef( null, workInProgress, Component, resolvedProps, renderLanes, ); - return child; - } - case MemoComponent: { - child = updateMemoComponent( + } else if ($$typeof === REACT_MEMO_TYPE) { + workInProgress.tag = MemoComponent; + return updateMemoComponent( null, workInProgress, Component, resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too renderLanes, ); - return child; } } + let hint = ''; if (__DEV__) { if ( @@ -1814,133 +1874,6 @@ function mountIncompleteClassComponent( ); } -function mountIndeterminateComponent( - _current: null | Fiber, - workInProgress: Fiber, - Component: $FlowFixMe, - renderLanes: Lanes, -) { - resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress); - - const props = workInProgress.pendingProps; - let context; - if (!disableLegacyContext) { - const unmaskedContext = getUnmaskedContext( - workInProgress, - Component, - false, - ); - context = getMaskedContext(workInProgress, unmaskedContext); - } - - prepareToReadContext(workInProgress, renderLanes); - let value; - let hasId; - - if (enableSchedulingProfiler) { - markComponentRenderStarted(workInProgress); - } - if (__DEV__) { - if ( - Component.prototype && - typeof Component.prototype.render === 'function' - ) { - const componentName = getComponentNameFromType(Component) || 'Unknown'; - - if (!didWarnAboutBadClass[componentName]) { - console.error( - "The <%s /> component appears to have a render method, but doesn't extend React.Component. " + - 'This is likely to cause errors. Change %s to extend React.Component instead.', - componentName, - componentName, - ); - didWarnAboutBadClass[componentName] = true; - } - } - - if (workInProgress.mode & StrictLegacyMode) { - ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null); - } - - setIsRendering(true); - ReactCurrentOwner.current = workInProgress; - value = renderWithHooks( - null, - workInProgress, - Component, - props, - context, - renderLanes, - ); - hasId = checkDidRenderIdHook(); - setIsRendering(false); - } else { - value = renderWithHooks( - null, - workInProgress, - Component, - props, - context, - renderLanes, - ); - hasId = checkDidRenderIdHook(); - } - if (enableSchedulingProfiler) { - markComponentRenderStopped(); - } - - // React DevTools reads this flag. - workInProgress.flags |= PerformedWork; - - if (__DEV__) { - // Support for module components is deprecated and is removed behind a flag. - // Whether or not it would crash later, we want to show a good message in DEV first. - if ( - typeof value === 'object' && - value !== null && - typeof value.render === 'function' && - value.$$typeof === undefined - ) { - const componentName = getComponentNameFromType(Component) || 'Unknown'; - if (!didWarnAboutModulePatternComponent[componentName]) { - console.error( - 'The <%s /> component appears to be a function component that returns a class instance. ' + - 'Change %s to a class that extends React.Component instead. ' + - "If you can't use a class try assigning the prototype on the function as a workaround. " + - "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + - 'cannot be called with `new` by React.', - componentName, - componentName, - componentName, - ); - didWarnAboutModulePatternComponent[componentName] = true; - } - } - } - - // Proceed under the assumption that this is a function component - workInProgress.tag = FunctionComponent; - if (__DEV__) { - if (disableLegacyContext && Component.contextTypes) { - console.error( - '%s uses the legacy contextTypes API which was removed in React 19. ' + - 'Use React.createContext() with React.useContext() instead.', - getComponentNameFromType(Component) || 'Unknown', - ); - } - } - - if (getIsHydrating() && hasId) { - pushMaterializedTreeId(workInProgress); - } - - reconcileChildren(null, workInProgress, value, renderLanes); - if (__DEV__) { - validateFunctionComponentInDev(workInProgress, Component); - } - return workInProgress.child; -} - function validateFunctionComponentInDev(workInProgress: Fiber, Component: any) { if (__DEV__) { if (Component) { @@ -3972,14 +3905,6 @@ function beginWork( workInProgress.lanes = NoLanes; switch (workInProgress.tag) { - case IndeterminateComponent: { - return mountIndeterminateComponent( - current, - workInProgress, - workInProgress.type, - renderLanes, - ); - } case LazyComponent: { const elementType = workInProgress.elementType; return mountLazyComponent( @@ -4102,6 +4027,21 @@ function beginWork( renderLanes, ); } + case IncompleteFunctionComponent: { + const Component = workInProgress.type; + const unresolvedProps = workInProgress.pendingProps; + const resolvedProps = + workInProgress.elementType === Component + ? unresolvedProps + : resolveDefaultProps(Component, unresolvedProps); + return mountIncompleteFunctionComponent( + current, + workInProgress, + Component, + resolvedProps, + renderLanes, + ); + } case SuspenseListComponent: { return updateSuspenseListComponent(current, workInProgress, renderLanes); } diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js index b64f9f9e626c8..47d1c3cfee476 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.js @@ -569,16 +569,6 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) { } } -function adoptClassInstance(workInProgress: Fiber, instance: any): void { - instance.updater = classComponentUpdater; - workInProgress.stateNode = instance; - // The instance needs access to the fiber so that it can schedule updates - setInstance(instance, workInProgress); - if (__DEV__) { - instance._reactInternalInstance = fakeInternalInstance; - } -} - function constructClassInstance( workInProgress: Fiber, ctor: any, @@ -659,7 +649,13 @@ function constructClassInstance( instance.state !== null && instance.state !== undefined ? instance.state : null); - adoptClassInstance(workInProgress, instance); + instance.updater = classComponentUpdater; + workInProgress.stateNode = instance; + // The instance needs access to the fiber so that it can schedule updates + setInstance(instance, workInProgress); + if (__DEV__) { + instance._reactInternalInstance = fakeInternalInstance; + } if (__DEV__) { if (typeof ctor.getDerivedStateFromProps === 'function' && state === null) { diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.js b/packages/react-reconciler/src/ReactFiberCompleteWork.js index 89044182672ad..01087e3696279 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.js @@ -45,7 +45,6 @@ import { import {now} from './Scheduler'; import { - IndeterminateComponent, FunctionComponent, ClassComponent, HostRoot, @@ -66,6 +65,7 @@ import { SimpleMemoComponent, LazyComponent, IncompleteClassComponent, + IncompleteFunctionComponent, ScopeComponent, OffscreenComponent, LegacyHiddenComponent, @@ -949,10 +949,10 @@ function completeWork( // for hydration. popTreeContext(workInProgress); switch (workInProgress.tag) { - case IndeterminateComponent: case LazyComponent: case SimpleMemoComponent: case FunctionComponent: + case IncompleteFunctionComponent: case ForwardRef: case Fragment: case Mode: diff --git a/packages/react-reconciler/src/ReactFiberComponentStack.js b/packages/react-reconciler/src/ReactFiberComponentStack.js index 36e22e8a9b1f2..f292cb51d10b4 100644 --- a/packages/react-reconciler/src/ReactFiberComponentStack.js +++ b/packages/react-reconciler/src/ReactFiberComponentStack.js @@ -17,7 +17,6 @@ import { SuspenseComponent, SuspenseListComponent, FunctionComponent, - IndeterminateComponent, ForwardRef, SimpleMemoComponent, ClassComponent, @@ -47,7 +46,6 @@ function describeFiber(fiber: Fiber): string { case SuspenseListComponent: return describeBuiltInComponentFrame('SuspenseList', owner); case FunctionComponent: - case IndeterminateComponent: case SimpleMemoComponent: return describeFunctionComponentFrame(fiber.type, owner); case ForwardRef: diff --git a/packages/react-reconciler/src/ReactFiberHydrationDiffs.js b/packages/react-reconciler/src/ReactFiberHydrationDiffs.js index 812d9d046a533..021da8abf33f1 100644 --- a/packages/react-reconciler/src/ReactFiberHydrationDiffs.js +++ b/packages/react-reconciler/src/ReactFiberHydrationDiffs.js @@ -17,7 +17,6 @@ import { SuspenseComponent, SuspenseListComponent, FunctionComponent, - IndeterminateComponent, ForwardRef, SimpleMemoComponent, ClassComponent, @@ -87,7 +86,6 @@ function describeFiberType(fiber: Fiber): null | string { case SuspenseListComponent: return 'SuspenseList'; case FunctionComponent: - case IndeterminateComponent: case SimpleMemoComponent: const fn = fiber.type; return fn.displayName || fn.name || null; diff --git a/packages/react-reconciler/src/ReactFiberThrow.js b/packages/react-reconciler/src/ReactFiberThrow.js index 3bb54f73e80d6..5b40a872b1f85 100644 --- a/packages/react-reconciler/src/ReactFiberThrow.js +++ b/packages/react-reconciler/src/ReactFiberThrow.js @@ -20,6 +20,7 @@ import { ClassComponent, HostRoot, IncompleteClassComponent, + IncompleteFunctionComponent, FunctionComponent, ForwardRef, SimpleMemoComponent, @@ -262,6 +263,13 @@ function markSuspenseBoundaryShouldCapture( update.tag = ForceUpdate; enqueueUpdate(sourceFiber, update, SyncLane); } + } else if (sourceFiber.tag === FunctionComponent) { + const currentSourceFiber = sourceFiber.alternate; + if (currentSourceFiber === null) { + // This is a new mount. Change the tag so it's not mistaken for a + // completed function component. + sourceFiber.tag = IncompleteFunctionComponent; + } } // The source fiber did not complete. Mark it with Sync priority to diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index ea8869a4a8c13..2570f857c7d2d 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -90,7 +90,6 @@ import { } from './ReactTypeOfMode'; import { HostRoot, - IndeterminateComponent, ClassComponent, SuspenseComponent, SuspenseListComponent, @@ -2395,12 +2394,6 @@ function replaySuspendedUnitOfWork(unitOfWork: Fiber): void { startProfilerTimer(unitOfWork); } switch (unitOfWork.tag) { - case IndeterminateComponent: { - // Because it suspended with `use`, we can assume it's a - // function component. - unitOfWork.tag = FunctionComponent; - // Fallthrough to the next branch. - } case SimpleMemoComponent: case FunctionComponent: { // Resolve `defaultProps`. This logic is copied from `beginWork`. @@ -3807,7 +3800,6 @@ export function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber: Fiber) { const tag = fiber.tag; if ( - tag !== IndeterminateComponent && tag !== HostRoot && tag !== ClassComponent && tag !== FunctionComponent && diff --git a/packages/react-reconciler/src/ReactWorkTags.js b/packages/react-reconciler/src/ReactWorkTags.js index 8e928d671dc87..a4d4eb1751548 100644 --- a/packages/react-reconciler/src/ReactWorkTags.js +++ b/packages/react-reconciler/src/ReactWorkTags.js @@ -35,11 +35,11 @@ export type WorkTag = | 24 | 25 | 26 - | 27; + | 27 + | 28; export const FunctionComponent = 0; export const ClassComponent = 1; -export const IndeterminateComponent = 2; // Before we know whether it is function or class export const HostRoot = 3; // Root of a host tree. Could be nested inside another node. export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer. export const HostComponent = 5; @@ -64,3 +64,4 @@ export const CacheComponent = 24; export const TracingMarkerComponent = 25; export const HostHoistable = 26; export const HostSingleton = 27; +export const IncompleteFunctionComponent = 28; diff --git a/packages/react-reconciler/src/__tests__/ReactSubtreeFlagsWarning-test.js b/packages/react-reconciler/src/__tests__/ReactSubtreeFlagsWarning-test.js index 49bde67837cdf..e5d9c9c445dad 100644 --- a/packages/react-reconciler/src/__tests__/ReactSubtreeFlagsWarning-test.js +++ b/packages/react-reconciler/src/__tests__/ReactSubtreeFlagsWarning-test.js @@ -132,11 +132,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { // @gate experimental || www it('regression: false positive for legacy suspense', async () => { - // Wrapping in memo because regular function components go through the - // mountIndeterminateComponent path, which acts like there's no `current` - // fiber even though there is. `memo` is not indeterminate, so it goes - // through the update path. - const Child = React.memo(({text}) => { + const Child = ({text}) => { // If text hasn't resolved, this will throw and exit before the passive // static effect flag is added by the useEffect call below. readText(text); @@ -147,7 +143,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { Scheduler.log(text); return text; - }); + }; function App() { return ( diff --git a/packages/react-reconciler/src/getComponentNameFromFiber.js b/packages/react-reconciler/src/getComponentNameFromFiber.js index 1a8464835ce4f..9eb7fdf4f8907 100644 --- a/packages/react-reconciler/src/getComponentNameFromFiber.js +++ b/packages/react-reconciler/src/getComponentNameFromFiber.js @@ -18,7 +18,6 @@ import { import { FunctionComponent, ClassComponent, - IndeterminateComponent, HostRoot, HostPortal, HostComponent, @@ -128,7 +127,6 @@ export default function getComponentNameFromFiber(fiber: Fiber): string | null { case ClassComponent: case FunctionComponent: case IncompleteClassComponent: - case IndeterminateComponent: case MemoComponent: case SimpleMemoComponent: if (typeof type === 'function') { diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index 788544b731e12..a06ef06e8bb2b 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -1410,7 +1410,6 @@ function renderClassComponent( } const didWarnAboutBadClass: {[string]: boolean} = {}; -const didWarnAboutModulePatternComponent: {[string]: boolean} = {}; const didWarnAboutContextTypeOnFunctionComponent: {[string]: boolean} = {}; const didWarnAboutGetDerivedStateOnFunctionComponent: {[string]: boolean} = {}; let didWarnAboutReassigningProps = false; @@ -1418,9 +1417,7 @@ const didWarnAboutDefaultPropsOnFunctionComponent: {[string]: boolean} = {}; let didWarnAboutGenerators = false; let didWarnAboutMaps = false; -// This would typically be a function component but we still support module pattern -// components for some reason. -function renderIndeterminateComponent( +function renderFunctionComponent( request: Request, task: Task, keyPath: KeyNode, @@ -1465,33 +1462,6 @@ function renderIndeterminateComponent( const actionStateCount = getActionStateCount(); const actionStateMatchingIndex = getActionStateMatchingIndex(); - if (__DEV__) { - // Support for module components is deprecated and is removed behind a flag. - // Whether or not it would crash later, we want to show a good message in DEV first. - if ( - typeof value === 'object' && - value !== null && - typeof value.render === 'function' && - value.$$typeof === undefined - ) { - const componentName = getComponentNameFromType(Component) || 'Unknown'; - if (!didWarnAboutModulePatternComponent[componentName]) { - console.error( - 'The <%s /> component appears to be a function component that returns a class instance. ' + - 'Change %s to a class that extends React.Component instead. ' + - "If you can't use a class try assigning the prototype on the function as a workaround. " + - "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + - 'cannot be called with `new` by React.', - componentName, - componentName, - componentName, - ); - didWarnAboutModulePatternComponent[componentName] = true; - } - } - } - - // Proceed under the assumption that this is a function component if (__DEV__) { if (disableLegacyContext && Component.contextTypes) { console.error( @@ -1817,7 +1787,7 @@ function renderElement( renderClassComponent(request, task, keyPath, type, props); return; } else { - renderIndeterminateComponent(request, task, keyPath, type, props); + renderFunctionComponent(request, task, keyPath, type, props); return; } }