diff --git a/packages/react-devtools-shared/src/backend/renderer.js b/packages/react-devtools-shared/src/backend/renderer.js
index b1e5dac527e61..58a6717615492 100644
--- a/packages/react-devtools-shared/src/backend/renderer.js
+++ b/packages/react-devtools-shared/src/backend/renderer.js
@@ -225,7 +225,7 @@ export function getInternalReactConstants(version: string): {
HostSingleton: 27, // Same as above
HostText: 6,
IncompleteClassComponent: 17,
- IndeterminateComponent: 2,
+ IndeterminateComponent: 2, // removed in 19.0.0
LazyComponent: 16,
LegacyHiddenComponent: 23,
MemoComponent: 14,
diff --git a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
index d07ab9e71b0b3..f89e95bcf5dde 100644
--- a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
+++ b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
@@ -14,7 +14,6 @@ let act;
let React;
let ReactDOM;
let ReactDOMClient;
-let PropTypes;
let findDOMNode;
const clone = function (o) {
@@ -99,7 +98,6 @@ describe('ReactComponentLifeCycle', () => {
findDOMNode =
ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.findDOMNode;
ReactDOMClient = require('react-dom/client');
- PropTypes = require('prop-types');
});
it('should not reuse an instance when it has been unmounted', async () => {
@@ -1114,72 +1112,6 @@ describe('ReactComponentLifeCycle', () => {
});
});
- if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
- // @gate !disableLegacyContext
- it('calls effects on module-pattern component', async () => {
- const log = [];
-
- function Parent() {
- return {
- render() {
- expect(typeof this.props).toBe('object');
- log.push('render');
- return ;
- },
- UNSAFE_componentWillMount() {
- log.push('will mount');
- },
- componentDidMount() {
- log.push('did mount');
- },
- componentDidUpdate() {
- log.push('did update');
- },
- getChildContext() {
- return {x: 2};
- },
- };
- }
- Parent.childContextTypes = {
- x: PropTypes.number,
- };
- function Child(props, context) {
- expect(context.x).toBe(2);
- return
;
- }
- Child.contextTypes = {
- x: PropTypes.number,
- };
-
- const root = ReactDOMClient.createRoot(document.createElement('div'));
- await expect(async () => {
- await act(() => {
- root.render( c && log.push('ref')} />);
- });
- }).toErrorDev(
- 'Warning: The component appears to be a function component that returns a class instance. ' +
- 'Change Parent 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. " +
- '`Parent.prototype = React.Component.prototype`. ' +
- "Don't use an arrow function since it cannot be called with `new` by React.",
- );
- await act(() => {
- root.render( c && log.push('ref')} />);
- });
-
- expect(log).toEqual([
- 'will mount',
- 'render',
- 'did mount',
- 'ref',
-
- 'render',
- 'did update',
- 'ref',
- ]);
- });
- }
-
it('should warn if getDerivedStateFromProps returns undefined', async () => {
class MyComponent extends React.Component {
state = {};
diff --git a/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js b/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
index 561928b24faf3..2e56a911a0c38 100644
--- a/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
+++ b/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
@@ -211,63 +211,27 @@ describe('ReactCompositeComponent', () => {
});
});
- if (require('shared/ReactFeatureFlags').disableModulePatternComponents) {
- it('should not support module pattern components', async () => {
- function Child({test}) {
- return {
- render() {
- return {test}
;
- },
- };
- }
-
- 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.",
- );
-
- expect(el.textContent).toBe('');
- });
- } else {
- it('should support module pattern components', () => {
- function Child({test}) {
- return {
- render() {
- return {test}
;
- },
- };
- }
+ it('should not support module pattern components', async () => {
+ function Child({test}) {
+ return {
+ render() {
+ return {test}
;
+ },
+ };
+ }
- const el = document.createElement('div');
- const root = ReactDOMClient.createRoot(el);
- expect(() => {
- ReactDOM.flushSync(() => {
- root.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.",
- );
+ const el = document.createElement('div');
+ const root = ReactDOMClient.createRoot(el);
+ await expect(async () => {
+ await act(() => {
+ root.render();
+ });
+ }).rejects.toThrow(
+ 'Objects are not valid as a React child (found: object with keys {render}).',
+ );
- expect(el.textContent).toBe('test');
- });
- }
+ expect(el.textContent).toBe('');
+ });
it('should use default values for undefined props', async () => {
class Component extends React.Component {
diff --git a/packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js b/packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js
index a1d3d28533fe9..ecb30f0f1d78e 100644
--- a/packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js
+++ b/packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js
@@ -527,72 +527,6 @@ describe('ReactCompositeComponent-state', () => {
]);
});
- if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
- it('should support stateful module pattern components', async () => {
- function Child() {
- return {
- state: {
- count: 123,
- },
- render() {
- return {`count:${this.state.count}`}
;
- },
- };
- }
-
- const el = document.createElement('div');
- const root = ReactDOMClient.createRoot(el);
- expect(() => {
- ReactDOM.flushSync(() => {
- root.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.",
- );
-
- expect(el.textContent).toBe('count:123');
- });
-
- it('should support getDerivedStateFromProps for module pattern components', async () => {
- function Child() {
- return {
- state: {
- count: 1,
- },
- render() {
- return {`count:${this.state.count}`}
;
- },
- };
- }
- Child.getDerivedStateFromProps = (props, prevState) => {
- return {
- count: prevState.count + props.incrementBy,
- };
- };
-
- const el = document.createElement('div');
- const root = ReactDOMClient.createRoot(el);
- await act(() => {
- root.render();
- });
-
- expect(el.textContent).toBe('count:1');
- await act(() => {
- root.render();
- });
- expect(el.textContent).toBe('count:3');
-
- await act(() => {
- root.render();
- });
- expect(el.textContent).toBe('count:4');
- });
- }
-
it('should not support setState in componentWillUnmount', async () => {
let subscription;
class A extends React.Component {
diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js
index a66cd12cd9178..f492aebb455db 100644
--- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js
@@ -627,23 +627,9 @@ describe('ReactDOMServerIntegration', () => {
checkFooDiv(await render());
});
- if (require('shared/ReactFeatureFlags').disableModulePatternComponents) {
- itThrowsWhenRendering(
- 'factory components',
- async render => {
- const FactoryComponent = () => {
- return {
- render: function () {
- return foo
;
- },
- };
- };
- await render(, 1);
- },
- 'Objects are not valid as a React child (found: object with keys {render})',
- );
- } else {
- itRenders('factory components', async render => {
+ itThrowsWhenRendering(
+ 'factory components',
+ async render => {
const FactoryComponent = () => {
return {
render: function () {
@@ -651,9 +637,10 @@ describe('ReactDOMServerIntegration', () => {
},
};
};
- checkFooDiv(await render(, 1));
- });
- }
+ await render(, 1);
+ },
+ 'Objects are not valid as a React child (found: object with keys {render})',
+ );
});
describe('component hierarchies', function () {
diff --git a/packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js b/packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js
index 36a227c0fabab..ffa923de3de58 100644
--- a/packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js
+++ b/packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js
@@ -879,56 +879,6 @@ describe('ReactErrorBoundaries', () => {
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
});
- // @gate !disableModulePatternComponents
- it('renders an error state if module-style context provider throws in componentWillMount', async () => {
- function BrokenComponentWillMountWithContext() {
- return {
- getChildContext() {
- return {foo: 42};
- },
- render() {
- return {this.props.children}
;
- },
- UNSAFE_componentWillMount() {
- throw new Error('Hello');
- },
- };
- }
- BrokenComponentWillMountWithContext.childContextTypes = {
- foo: PropTypes.number,
- };
-
- const container = document.createElement('div');
- const root = ReactDOMClient.createRoot(container);
-
- await expect(async () => {
- await act(() => {
- root.render(
-
-
- ,
- );
- });
- }).toErrorDev([
- 'Warning: The component appears to be a function component that ' +
- 'returns a class instance. ' +
- 'Change BrokenComponentWillMountWithContext 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. " +
- '`BrokenComponentWillMountWithContext.prototype = React.Component.prototype`. ' +
- "Don't use an arrow function since it cannot be called with `new` by React.",
- ...gate(flags =>
- flags.disableLegacyContext
- ? [
- 'Warning: BrokenComponentWillMountWithContext uses the legacy childContextTypes API which was removed in React 19. Use React.createContext() instead.',
- 'Warning: BrokenComponentWillMountWithContext uses the legacy childContextTypes API which was removed in React 19. Use React.createContext() instead.',
- ]
- : [],
- ),
- ]);
-
- expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
- });
-
it('mounts the error message if mounting fails', async () => {
function renderError(error) {
return ;
diff --git a/packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js b/packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js
index 8c53de16bf814..b0b223dd43bee 100644
--- a/packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js
+++ b/packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js
@@ -849,54 +849,6 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
});
- if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
- // @gate !disableLegacyMode
- it('renders an error state if module-style context provider throws in componentWillMount', () => {
- function BrokenComponentWillMountWithContext() {
- return {
- getChildContext() {
- return {foo: 42};
- },
- render() {
- return {this.props.children}
;
- },
- UNSAFE_componentWillMount() {
- throw new Error('Hello');
- },
- };
- }
- BrokenComponentWillMountWithContext.childContextTypes = {
- foo: PropTypes.number,
- };
-
- const container = document.createElement('div');
- expect(() =>
- ReactDOM.render(
-
-
- ,
- container,
- ),
- ).toErrorDev([
- 'Warning: The component appears to be a function component that ' +
- 'returns a class instance. ' +
- 'Change BrokenComponentWillMountWithContext 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. " +
- '`BrokenComponentWillMountWithContext.prototype = React.Component.prototype`. ' +
- "Don't use an arrow function since it cannot be called with `new` by React.",
- ...gate(flags =>
- flags.disableLegacyContext
- ? [
- 'Warning: BrokenComponentWillMountWithContext uses the legacy childContextTypes API which was removed in React 19. Use React.createContext() instead.',
- 'Warning: BrokenComponentWillMountWithContext uses the legacy childContextTypes API which was removed in React 19. Use React.createContext() instead.',
- ]
- : [],
- ),
- ]);
- expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
- });
- }
-
// @gate !disableLegacyMode
it('mounts the error message if mounting fails', () => {
function renderError(error) {
diff --git a/packages/react-dom/src/__tests__/refs-test.js b/packages/react-dom/src/__tests__/refs-test.js
index f43ada19e004f..4a638ef17c566 100644
--- a/packages/react-dom/src/__tests__/refs-test.js
+++ b/packages/react-dom/src/__tests__/refs-test.js
@@ -11,7 +11,6 @@
let React = require('react');
let ReactDOMClient = require('react-dom/client');
-let ReactFeatureFlags = require('shared/ReactFeatureFlags');
let act = require('internal-test-utils').act;
// This is testing if string refs are deleted from `instance.refs`
@@ -24,7 +23,6 @@ describe('reactiverefs', () => {
jest.resetModules();
React = require('react');
ReactDOMClient = require('react-dom/client');
- ReactFeatureFlags = require('shared/ReactFeatureFlags');
act = require('internal-test-utils').act;
});
@@ -195,38 +193,6 @@ describe('reactiverefs', () => {
});
});
-if (!ReactFeatureFlags.disableModulePatternComponents) {
- describe('factory components', () => {
- it('Should correctly get the ref', async () => {
- function Comp() {
- return {
- elemRef: React.createRef(),
- render() {
- return ;
- },
- };
- }
-
- let inst;
- await expect(async () => {
- const container = document.createElement('div');
- const root = ReactDOMClient.createRoot(container);
-
- await act(() => {
- root.render( (inst = current)} />);
- });
- }).toErrorDev(
- 'Warning: The component appears to be a function component that returns a class instance. ' +
- 'Change Comp 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. " +
- '`Comp.prototype = React.Component.prototype`. ' +
- "Don't use an arrow function since it cannot be called with `new` by React.",
- );
- expect(inst.elemRef.current.tagName).toBe('DIV');
- });
- });
-}
-
/**
* Tests that when a ref hops around children, we can track that correctly.
*/
@@ -236,7 +202,6 @@ describe('ref swapping', () => {
jest.resetModules();
React = require('react');
ReactDOMClient = require('react-dom/client');
- ReactFeatureFlags = require('shared/ReactFeatureFlags');
act = require('internal-test-utils').act;
RefHopsAround = class extends React.Component {
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 a3f2734957f25..dea59ecb8b03e 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,
@@ -95,7 +94,6 @@ import ReactSharedInternals from 'shared/ReactSharedInternals';
import {
debugRenderPhaseSideEffectsForStrictMode,
disableLegacyContext,
- disableModulePatternComponents,
enableProfilerCommitHooks,
enableProfilerTimer,
enableScopeAPI,
@@ -115,7 +113,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,
@@ -236,7 +239,6 @@ import {
queueHydrationError,
} from './ReactFiberHydrationContext';
import {
- adoptClassInstance,
constructClassInstance,
mountClassInstance,
resumeMountClassInstance,
@@ -244,12 +246,12 @@ import {
} from './ReactFiberClassComponent';
import {resolveDefaultProps} from './ReactFiberLazyComponent';
import {
- resolveLazyComponentTag,
createFiberFromTypeAndProps,
createFiberFromFragment,
createFiberFromOffscreen,
createWorkInProgress,
isSimpleFunctionComponent,
+ isFunctionClassComponent,
} from './ReactFiber';
import {
retryDehydratedSuspenseBoundary,
@@ -305,7 +307,6 @@ export const SelectiveHydrationException: mixed = new Error(
let didReceiveUpdate: boolean = false;
let didWarnAboutBadClass;
-let didWarnAboutModulePatternComponent;
let didWarnAboutContextTypeOnFunctionComponent;
let didWarnAboutGetDerivedStateOnFunctionComponent;
let didWarnAboutFunctionRefs;
@@ -316,7 +317,6 @@ let didWarnAboutDefaultPropsOnFunctionComponent;
if (__DEV__) {
didWarnAboutBadClass = ({}: {[string]: boolean});
- didWarnAboutModulePatternComponent = ({}: {[string]: boolean});
didWarnAboutContextTypeOnFunctionComponent = ({}: {[string]: boolean});
didWarnAboutGetDerivedStateOnFunctionComponent = ({}: {[string]: boolean});
didWarnAboutFunctionRefs = ({}: {[string]: boolean});
@@ -1053,6 +1053,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);
@@ -1700,64 +1737,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 (
@@ -1817,194 +1854,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;
- }
- }
- }
-
- if (
- // Run these checks in production only if the flag is off.
- // Eventually we'll delete this branch altogether.
- !disableModulePatternComponents &&
- typeof value === 'object' &&
- value !== null &&
- typeof value.render === 'function' &&
- value.$$typeof === undefined
- ) {
- if (__DEV__) {
- 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 class instance
- workInProgress.tag = ClassComponent;
-
- // Throw out any hooks that were used.
- workInProgress.memoizedState = null;
- workInProgress.updateQueue = null;
-
- // Push context providers early to prevent context stack mismatches.
- // During mounting we don't know the child context yet as the instance doesn't exist.
- // We will invalidate the child context in finishClassComponent() right after rendering.
- let hasContext = false;
- if (isLegacyContextProvider(Component)) {
- hasContext = true;
- pushLegacyContextProvider(workInProgress);
- } else {
- hasContext = false;
- }
-
- workInProgress.memoizedState =
- value.state !== null && value.state !== undefined ? value.state : null;
-
- initializeUpdateQueue(workInProgress);
-
- adoptClassInstance(workInProgress, value);
- mountClassInstance(workInProgress, Component, props, renderLanes);
- return finishClassComponent(
- null,
- workInProgress,
- Component,
- true,
- hasContext,
- renderLanes,
- );
- } else {
- // 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) {
@@ -4028,14 +3877,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(
diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js
index 231e6a3508e4e..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) {
@@ -1230,7 +1226,6 @@ function updateClassInstance(
}
export {
- adoptClassInstance,
constructClassInstance,
mountClassInstance,
resumeMountClassInstance,
diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.js b/packages/react-reconciler/src/ReactFiberCompleteWork.js
index 89044182672ad..a07a9739016a9 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,
@@ -949,7 +948,6 @@ function completeWork(
// for hydration.
popTreeContext(workInProgress);
switch (workInProgress.tag) {
- case IndeterminateComponent:
case LazyComponent:
case SimpleMemoComponent:
case FunctionComponent:
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/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..bc6782b02f610 100644
--- a/packages/react-reconciler/src/ReactWorkTags.js
+++ b/packages/react-reconciler/src/ReactWorkTags.js
@@ -39,7 +39,6 @@ export type WorkTag =
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;
diff --git a/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js b/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js
index 5bfa66d66fab9..b63a8b23476e4 100644
--- a/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js
@@ -1308,16 +1308,6 @@ describe('ReactHooks', () => {
return ;
});
- function Factory() {
- return {
- state: {},
- render() {
- renderCount++;
- return ;
- },
- };
- }
-
let renderer;
await act(() => {
renderer = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
@@ -1410,46 +1400,6 @@ describe('ReactHooks', () => {
});
expect(renderCount).toBe(__DEV__ ? 2 : 1);
- if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
- renderCount = 0;
- await expect(async () => {
- await act(() => {
- renderer.update();
- });
- }).toErrorDev(
- 'Warning: The component appears to be a function component that returns a class instance. ' +
- 'Change Factory 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. " +
- '`Factory.prototype = React.Component.prototype`. ' +
- "Don't use an arrow function since it cannot be called with `new` by React.",
- );
- expect(renderCount).toBe(1);
- renderCount = 0;
- await act(() => {
- renderer.update();
- });
- expect(renderCount).toBe(1);
-
- renderCount = 0;
- await act(() => {
- renderer.update(
-
-
- ,
- );
- });
- expect(renderCount).toBe(__DEV__ ? 2 : 1); // Treated like a class
- renderCount = 0;
- await act(() => {
- renderer.update(
-
-
- ,
- );
- });
- expect(renderCount).toBe(__DEV__ ? 2 : 1); // Treated like a class
- }
-
renderCount = 0;
await act(() => {
renderer.update();
diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
index 45b223e8106a4..04e7be86c61cf 100644
--- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
@@ -227,44 +227,6 @@ describe('ReactHooksWithNoopRenderer', () => {
await waitForAll([10]);
});
- // @gate !disableModulePatternComponents
- it('throws inside module-style components', async () => {
- function Counter() {
- return {
- render() {
- const [count] = useState(0);
- return ;
- },
- };
- }
- ReactNoop.render();
- await expect(
- async () =>
- await waitForThrow(
- 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen ' +
- 'for one of the following reasons:\n' +
- '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
- '2. You might be breaking the Rules of Hooks\n' +
- '3. You might have more than one copy of React in the same app\n' +
- 'See https://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem.',
- ),
- ).toErrorDev(
- 'Warning: The component appears to be a function component that returns a class instance. ' +
- 'Change Counter 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. " +
- '`Counter.prototype = React.Component.prototype`. ' +
- "Don't use an arrow function since it cannot be called with `new` by React.",
- );
-
- // Confirm that a subsequent hook works properly.
- function GoodCounter(props) {
- const [count] = useState(props.initialCount);
- return ;
- }
- ReactNoop.render();
- await waitForAll([10]);
- });
-
it('throws when called outside the render phase', async () => {
expect(() => {
expect(() => useState(0)).toThrow(
diff --git a/packages/react-reconciler/src/__tests__/ReactIncremental-test.js b/packages/react-reconciler/src/__tests__/ReactIncremental-test.js
index 13f904bf9d014..4beb0a12dabb2 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncremental-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncremental-test.js
@@ -1864,48 +1864,6 @@ describe('ReactIncremental', () => {
]);
});
- // @gate !disableModulePatternComponents
- // @gate !disableLegacyContext
- it('does not leak own context into context provider (factory components)', async () => {
- function Recurse(props, context) {
- return {
- getChildContext() {
- return {n: (context.n || 3) - 1};
- },
- render() {
- Scheduler.log('Recurse ' + JSON.stringify(context));
- if (context.n === 0) {
- return null;
- }
- return ;
- },
- };
- }
- Recurse.contextTypes = {
- n: PropTypes.number,
- };
- Recurse.childContextTypes = {
- n: PropTypes.number,
- };
-
- ReactNoop.render();
- await expect(
- async () =>
- await waitForAll([
- 'Recurse {}',
- 'Recurse {"n":2}',
- 'Recurse {"n":1}',
- 'Recurse {"n":0}',
- ]),
- ).toErrorDev([
- 'Warning: The component appears to be a function component that returns a class instance. ' +
- 'Change Recurse 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. " +
- '`Recurse.prototype = React.Component.prototype`. ' +
- "Don't use an arrow function since it cannot be called with `new` by React.",
- ]);
- });
-
// @gate www
// @gate !disableLegacyContext
it('provides context when reusing work', async () => {
diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
index 6d86507d5bdec..c6e342871b198 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
@@ -1754,45 +1754,6 @@ describe('ReactIncrementalErrorHandling', () => {
);
});
- // @gate !disableModulePatternComponents
- it('handles error thrown inside getDerivedStateFromProps of a module-style context provider', async () => {
- function Provider() {
- return {
- getChildContext() {
- return {foo: 'bar'};
- },
- render() {
- return 'Hi';
- },
- };
- }
- Provider.childContextTypes = {
- x: () => {},
- };
- Provider.getDerivedStateFromProps = () => {
- throw new Error('Oops!');
- };
-
- ReactNoop.render();
- await expect(async () => {
- await waitForThrow('Oops!');
- }).toErrorDev([
- 'Warning: The component appears to be a function component that returns a class instance. ' +
- 'Change Provider 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. " +
- '`Provider.prototype = React.Component.prototype`. ' +
- "Don't use an arrow function since it cannot be called with `new` by React.",
- ...gate(flags =>
- flags.disableLegacyContext
- ? [
- 'Warning: Provider uses the legacy childContextTypes API which was removed in React 19. Use React.createContext() instead.',
- 'Warning: Provider uses the legacy childContextTypes API which was removed in React 19. Use React.createContext() instead.',
- ]
- : [],
- ),
- ]);
- });
-
it('uncaught errors should be discarded if the render is aborted', async () => {
const root = ReactNoop.createRoot();
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-refresh/src/__tests__/ReactFreshIntegration-test.js b/packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js
index ed8c56072a217..c1e5f308b2202 100644
--- a/packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js
+++ b/packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js
@@ -1639,54 +1639,6 @@ describe('ReactFreshIntegration', () => {
}
});
- if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
- it('remounts deprecated factory components', async () => {
- if (__DEV__) {
- await expect(async () => {
- await render(`
- function Parent() {
- return {
- render() {
- return ;
- }
- };
- };
-
- function Child({prop}) {
- return {prop}1
;
- };
-
- export default Parent;
- `);
- }).toErrorDev(
- 'The component appears to be a function component ' +
- 'that returns a class instance.',
- );
- const el = container.firstChild;
- expect(el.textContent).toBe('A1');
- await patch(`
- function Parent() {
- return {
- render() {
- return ;
- }
- };
- };
-
- function Child({prop}) {
- return {prop}2
;
- };
-
- export default Parent;
- `);
- // Like classes, factory components always remount.
- expect(container.firstChild).not.toBe(el);
- const newEl = container.firstChild;
- expect(newEl.textContent).toBe('B2');
- }
- });
- }
-
describe('with inline requires', () => {
beforeEach(() => {
global.FakeModuleSystem = {};
diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js
index c66608414d900..4567a1e80d13b 100644
--- a/packages/react-server/src/ReactFizzServer.js
+++ b/packages/react-server/src/ReactFizzServer.js
@@ -137,7 +137,6 @@ import {
import ReactSharedInternals from 'shared/ReactSharedInternals';
import {
disableLegacyContext,
- disableModulePatternComponents,
enableBigIntSupport,
enableScopeAPI,
enableSuspenseAvoidThisFallbackFizz,
@@ -1388,7 +1387,6 @@ function renderClassComponent(
}
const didWarnAboutBadClass: {[string]: boolean} = {};
-const didWarnAboutModulePatternComponent: {[string]: boolean} = {};
const didWarnAboutContextTypeOnFunctionComponent: {[string]: boolean} = {};
const didWarnAboutGetDerivedStateOnFunctionComponent: {[string]: boolean} = {};
let didWarnAboutReassigningProps = false;
@@ -1396,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,
@@ -1444,83 +1440,26 @@ function renderIndeterminateComponent(
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;
- }
+ 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 (
- // Run these checks in production only if the flag is off.
- // Eventually we'll delete this branch altogether.
- !disableModulePatternComponents &&
- typeof value === 'object' &&
- value !== null &&
- typeof value.render === 'function' &&
- value.$$typeof === undefined
- ) {
- if (__DEV__) {
- 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;
- }
- }
-
- mountClassInstance(value, Component, props, legacyContext);
- finishClassComponent(request, task, keyPath, value, Component, props);
- } else {
- // Proceed under the assumption that this is a function component
- 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 (__DEV__) {
- validateFunctionComponentInDev(Component);
- }
- finishFunctionComponent(
- request,
- task,
- keyPath,
- value,
- hasId,
- actionStateCount,
- actionStateMatchingIndex,
- );
+ if (__DEV__) {
+ validateFunctionComponentInDev(Component);
}
+ finishFunctionComponent(
+ request,
+ task,
+ keyPath,
+ value,
+ hasId,
+ actionStateCount,
+ actionStateMatchingIndex,
+ );
task.componentStack = previousComponentStack;
}
@@ -1825,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;
}
}
diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js
index 9747dd38f6691..3861ea5b6329c 100644
--- a/packages/shared/ReactFeatureFlags.js
+++ b/packages/shared/ReactFeatureFlags.js
@@ -206,8 +206,6 @@ export const enableRenderableContext = __NEXT_MAJOR__;
// when we plan to enable them.
// -----------------------------------------------------------------------------
-export const disableModulePatternComponents = __NEXT_MAJOR__;
-
export const enableUseRefAccessWarning = false;
// Enables time slicing for updates that aren't wrapped in startTransition.
diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js
index e51541b1bb93d..1c6180ae903ae 100644
--- a/packages/shared/forks/ReactFeatureFlags.native-fb.js
+++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js
@@ -34,7 +34,6 @@ export const {
} = dynamicFlags;
// The rest of the flags are static for better dead code elimination.
-export const disableModulePatternComponents = true;
export const enableDebugTracing = false;
export const enableAsyncDebugInfo = false;
export const enableSchedulingProfiler = __PROFILE__;
diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js
index d447207b98e80..1c3a95b52c40b 100644
--- a/packages/shared/forks/ReactFeatureFlags.native-oss.js
+++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js
@@ -31,7 +31,6 @@ export const enableDeferRootSchedulingToMicrotask = __TODO_NEXT_RN_MAJOR__;
export const alwaysThrottleRetries = __TODO_NEXT_RN_MAJOR__;
export const enableInfiniteRenderLoopDetection = __TODO_NEXT_RN_MAJOR__;
export const enableComponentStackLocations = __TODO_NEXT_RN_MAJOR__;
-export const disableModulePatternComponents = __TODO_NEXT_RN_MAJOR__;
// -----------------------------------------------------------------------------
// These are ready to flip after the next React npm release (or RN switches to
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js
index bce10070683f6..d5b60e8203396 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js
@@ -94,7 +94,6 @@ export const disableLegacyMode = __NEXT_MAJOR__;
export const disableLegacyContext = __NEXT_MAJOR__;
export const disableDOMTestUtils = __NEXT_MAJOR__;
export const enableNewBooleanProps = __NEXT_MAJOR__;
-export const disableModulePatternComponents = __NEXT_MAJOR__;
export const enableRenderableContext = __NEXT_MAJOR__;
export const enableReactTestRendererWarning = __NEXT_MAJOR__;
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
index b184d47d8fe3a..710eeb607ebfb 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
@@ -34,7 +34,6 @@ export const enableSuspenseCallback = false;
export const disableLegacyContext = false;
export const enableTrustedTypesIntegration = false;
export const disableTextareaChildren = false;
-export const disableModulePatternComponents = true;
export const enableComponentStackLocations = false;
export const enableLegacyFBSupport = false;
export const enableFilterEmptyStringAttributesDOM = true;
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
index 87b5e0302aea2..a4a3c138a218d 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
@@ -34,7 +34,6 @@ export const enableSuspenseCallback = true;
export const disableLegacyContext = false;
export const enableTrustedTypesIntegration = false;
export const disableTextareaChildren = false;
-export const disableModulePatternComponents = true;
export const enableSuspenseAvoidThisFallback = true;
export const enableSuspenseAvoidThisFallbackFizz = false;
export const enableCPUSuspense = false;
diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js
index 92c3eb0b38653..c309500c3f00b 100644
--- a/packages/shared/forks/ReactFeatureFlags.www.js
+++ b/packages/shared/forks/ReactFeatureFlags.www.js
@@ -82,8 +82,6 @@ export const enablePostpone = false;
// Need to remove it.
export const disableCommentsAsDOMContainers = false;
-export const disableModulePatternComponents = true;
-
export const enableCreateEventHandleAPI = true;
export const enableScopeAPI = true;