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 ce909b802530a..dbc2b42a7ea85 100644
--- a/packages/react-reconciler/src/ReactFiber.js
+++ b/packages/react-reconciler/src/ReactFiber.js
@@ -42,7 +42,6 @@ import {
import {NoFlags, Placement, StaticMask} from './ReactFiberFlags';
import {ConcurrentRoot} from './ReactRootTags';
import {
- IndeterminateComponent,
ClassComponent,
HostRoot,
HostComponent,
@@ -248,19 +247,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.
@@ -351,7 +341,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);
@@ -492,7 +481,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 f6dce003e842f..6d6a94ecae19a 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) {
@@ -3964,14 +3897,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(
@@ -4094,6 +4019,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 ce18234fd37ca..e0873b7367553 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 f9b18aff86485..41a7c8d7efa4d 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`.
@@ -3823,7 +3816,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 f49fca69b6485..4567a1e80d13b 100644
--- a/packages/react-server/src/ReactFizzServer.js
+++ b/packages/react-server/src/ReactFizzServer.js
@@ -1387,7 +1387,6 @@ function renderClassComponent(
}
const didWarnAboutBadClass: {[string]: boolean} = {};
-const didWarnAboutModulePatternComponent: {[string]: boolean} = {};
const didWarnAboutContextTypeOnFunctionComponent: {[string]: boolean} = {};
const didWarnAboutGetDerivedStateOnFunctionComponent: {[string]: boolean} = {};
let didWarnAboutReassigningProps = false;
@@ -1395,9 +1394,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,
@@ -1442,33 +1439,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(
@@ -1794,7 +1764,7 @@ function renderElement(
renderClassComponent(request, task, keyPath, type, props);
return;
} else {
- renderIndeterminateComponent(request, task, keyPath, type, props);
+ renderFunctionComponent(request, task, keyPath, type, props);
return;
}
}