@samitouri / QOS-React-1 / commits / cc56bed38c

Remove module pattern function component support (#27742)

The module pattern ``` function MyComponent() { return { render() { return this.state.foo } } } ``` has been deprecated for approximately 5 years now. This PR removes support for this pattern. It also simplifies a number of code paths in particular related to the concept of `IndeterminateComponent` types.

Josh Story committed Mar 28, 2024 at 13:08 UTC cc56bed38cbe5a5c76dfdc4e9c642fab4884a3fc
31 files changed +124 -923
packages/react-devtools-shared/src/backend/renderer.js
+1 -1
@@ -225,7 +225,7 @@ export function getInternalReactConstants(version: string): {
225 HostSingleton: 27, // Same as above
226 HostText: 6,
227 IncompleteClassComponent: 17,
228 - IndeterminateComponent: 2,
228 + IndeterminateComponent: 2, // removed in 19.0.0
229 LazyComponent: 16,
230 LegacyHiddenComponent: 23,
231 MemoComponent: 14,
packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
-68
@@ -14,7 +14,6 @@ let act;
14 let React;
15 let ReactDOM;
16 let ReactDOMClient;
17 -let PropTypes;
17 let findDOMNode;
18
19 const clone = function (o) {
@@ -99,7 +98,6 @@ describe('ReactComponentLifeCycle', () => {
98 findDOMNode =
99 ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.findDOMNode;
100 ReactDOMClient = require('react-dom/client');
102 - PropTypes = require('prop-types');
101 });
102
103 it('should not reuse an instance when it has been unmounted', async () => {
@@ -1114,72 +1112,6 @@ describe('ReactComponentLifeCycle', () => {
1112 });
1113 });
1114
1117 - if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
1118 - // @gate !disableLegacyContext
1119 - it('calls effects on module-pattern component', async () => {
1120 - const log = [];
1121 -
1122 - function Parent() {
1123 - return {
1124 - render() {
1125 - expect(typeof this.props).toBe('object');
1126 - log.push('render');
1127 - return <Child />;
1128 - },
1129 - UNSAFE_componentWillMount() {
1130 - log.push('will mount');
1131 - },
1132 - componentDidMount() {
1133 - log.push('did mount');
1134 - },
1135 - componentDidUpdate() {
1136 - log.push('did update');
1137 - },
1138 - getChildContext() {
1139 - return {x: 2};
1140 - },
1141 - };
1142 - }
1143 - Parent.childContextTypes = {
1144 - x: PropTypes.number,
1145 - };
1146 - function Child(props, context) {
1147 - expect(context.x).toBe(2);
1148 - return <div />;
1149 - }
1150 - Child.contextTypes = {
1151 - x: PropTypes.number,
1152 - };
1153 -
1154 - const root = ReactDOMClient.createRoot(document.createElement('div'));
1155 - await expect(async () => {
1156 - await act(() => {
1157 - root.render(<Parent ref={c => c && log.push('ref')} />);
1158 - });
1159 - }).toErrorDev(
1160 - 'Warning: The <Parent /> component appears to be a function component that returns a class instance. ' +
1161 - 'Change Parent to a class that extends React.Component instead. ' +
1162 - "If you can't use a class try assigning the prototype on the function as a workaround. " +
1163 - '`Parent.prototype = React.Component.prototype`. ' +
1164 - "Don't use an arrow function since it cannot be called with `new` by React.",
1165 - );
1166 - await act(() => {
1167 - root.render(<Parent ref={c => c && log.push('ref')} />);
1168 - });
1169 -
1170 - expect(log).toEqual([
1171 - 'will mount',
1172 - 'render',
1173 - 'did mount',
1174 - 'ref',
1175 -
1176 - 'render',
1177 - 'did update',
1178 - 'ref',
1179 - ]);
1180 - });
1181 - }
1182 -
1115 it('should warn if getDerivedStateFromProps returns undefined', async () => {
1116 class MyComponent extends React.Component {
1117 state = {};
packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
+19 -55
@@ -211,63 +211,27 @@ describe('ReactCompositeComponent', () => {
211 });
212 });
213
214 - if (require('shared/ReactFeatureFlags').disableModulePatternComponents) {
215 - it('should not support module pattern components', async () => {
216 - function Child({test}) {
217 - return {
218 - render() {
219 - return <div>{test}</div>;
220 - },
221 - };
222 - }
223 -
224 - const el = document.createElement('div');
225 - const root = ReactDOMClient.createRoot(el);
226 - await expect(async () => {
227 - await expect(async () => {
228 - await act(() => {
229 - root.render(<Child test="test" />);
230 - });
231 - }).rejects.toThrow(
232 - 'Objects are not valid as a React child (found: object with keys {render}).',
233 - );
234 - }).toErrorDev(
235 - 'Warning: The <Child /> component appears to be a function component that returns a class instance. ' +
236 - 'Change Child to a class that extends React.Component instead. ' +
237 - "If you can't use a class try assigning the prototype on the function as a workaround. " +
238 - '`Child.prototype = React.Component.prototype`. ' +
239 - "Don't use an arrow function since it cannot be called with `new` by React.",
240 - );
241 -
242 - expect(el.textContent).toBe('');
243 - });
244 - } else {
245 - it('should support module pattern components', () => {
246 - function Child({test}) {
247 - return {
248 - render() {
249 - return <div>{test}</div>;
250 - },
251 - };
252 - }
214 + it('should not support module pattern components', async () => {
215 + function Child({test}) {
216 + return {
217 + render() {
218 + return <div>{test}</div>;
219 + },
220 + };
221 + }
222
254 - const el = document.createElement('div');
255 - const root = ReactDOMClient.createRoot(el);
256 - expect(() => {
257 - ReactDOM.flushSync(() => {
258 - root.render(<Child test="test" />);
259 - });
260 - }).toErrorDev(
261 - 'Warning: The <Child /> component appears to be a function component that returns a class instance. ' +
262 - 'Change Child to a class that extends React.Component instead. ' +
263 - "If you can't use a class try assigning the prototype on the function as a workaround. " +
264 - '`Child.prototype = React.Component.prototype`. ' +
265 - "Don't use an arrow function since it cannot be called with `new` by React.",
266 - );
223 + const el = document.createElement('div');
224 + const root = ReactDOMClient.createRoot(el);
225 + await expect(async () => {
226 + await act(() => {
227 + root.render(<Child test="test" />);
228 + });
229 + }).rejects.toThrow(
230 + 'Objects are not valid as a React child (found: object with keys {render}).',
231 + );
232
268 - expect(el.textContent).toBe('test');
269 - });
270 - }
233 + expect(el.textContent).toBe('');
234 + });
235
236 it('should use default values for undefined props', async () => {
237 class Component extends React.Component {
packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js
-66
@@ -527,72 +527,6 @@ describe('ReactCompositeComponent-state', () => {
527 ]);
528 });
529
530 - if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
531 - it('should support stateful module pattern components', async () => {
532 - function Child() {
533 - return {
534 - state: {
535 - count: 123,
536 - },
537 - render() {
538 - return <div>{`count:${this.state.count}`}</div>;
539 - },
540 - };
541 - }
542 -
543 - const el = document.createElement('div');
544 - const root = ReactDOMClient.createRoot(el);
545 - expect(() => {
546 - ReactDOM.flushSync(() => {
547 - root.render(<Child />);
548 - });
549 - }).toErrorDev(
550 - 'Warning: The <Child /> component appears to be a function component that returns a class instance. ' +
551 - 'Change Child to a class that extends React.Component instead. ' +
552 - "If you can't use a class try assigning the prototype on the function as a workaround. " +
553 - '`Child.prototype = React.Component.prototype`. ' +
554 - "Don't use an arrow function since it cannot be called with `new` by React.",
555 - );
556 -
557 - expect(el.textContent).toBe('count:123');
558 - });
559 -
560 - it('should support getDerivedStateFromProps for module pattern components', async () => {
561 - function Child() {
562 - return {
563 - state: {
564 - count: 1,
565 - },
566 - render() {
567 - return <div>{`count:${this.state.count}`}</div>;
568 - },
569 - };
570 - }
571 - Child.getDerivedStateFromProps = (props, prevState) => {
572 - return {
573 - count: prevState.count + props.incrementBy,
574 - };
575 - };
576 -
577 - const el = document.createElement('div');
578 - const root = ReactDOMClient.createRoot(el);
579 - await act(() => {
580 - root.render(<Child incrementBy={0} />);
581 - });
582 -
583 - expect(el.textContent).toBe('count:1');
584 - await act(() => {
585 - root.render(<Child incrementBy={2} />);
586 - });
587 - expect(el.textContent).toBe('count:3');
588 -
589 - await act(() => {
590 - root.render(<Child incrementBy={1} />);
591 - });
592 - expect(el.textContent).toBe('count:4');
593 - });
594 - }
595 -
530 it('should not support setState in componentWillUnmount', async () => {
531 let subscription;
532 class A extends React.Component {
packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js
+7 -20
@@ -627,23 +627,9 @@ describe('ReactDOMServerIntegration', () => {
627 checkFooDiv(await render(<ClassComponent />));
628 });
629
630 - if (require('shared/ReactFeatureFlags').disableModulePatternComponents) {
631 - itThrowsWhenRendering(
632 - 'factory components',
633 - async render => {
634 - const FactoryComponent = () => {
635 - return {
636 - render: function () {
637 - return <div>foo</div>;
638 - },
639 - };
640 - };
641 - await render(<FactoryComponent />, 1);
642 - },
643 - 'Objects are not valid as a React child (found: object with keys {render})',
644 - );
645 - } else {
646 - itRenders('factory components', async render => {
630 + itThrowsWhenRendering(
631 + 'factory components',
632 + async render => {
633 const FactoryComponent = () => {
634 return {
635 render: function () {
@@ -651,9 +637,10 @@ describe('ReactDOMServerIntegration', () => {
637 },
638 };
639 };
654 - checkFooDiv(await render(<FactoryComponent />, 1));
655 - });
656 - }
640 + await render(<FactoryComponent />, 1);
641 + },
642 + 'Objects are not valid as a React child (found: object with keys {render})',
643 + );
644 });
645
646 describe('component hierarchies', function () {
packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js
-50
@@ -879,56 +879,6 @@ describe('ReactErrorBoundaries', () => {
879 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
880 });
881
882 - // @gate !disableModulePatternComponents
883 - it('renders an error state if module-style context provider throws in componentWillMount', async () => {
884 - function BrokenComponentWillMountWithContext() {
885 - return {
886 - getChildContext() {
887 - return {foo: 42};
888 - },
889 - render() {
890 - return <div>{this.props.children}</div>;
891 - },
892 - UNSAFE_componentWillMount() {
893 - throw new Error('Hello');
894 - },
895 - };
896 - }
897 - BrokenComponentWillMountWithContext.childContextTypes = {
898 - foo: PropTypes.number,
899 - };
900 -
901 - const container = document.createElement('div');
902 - const root = ReactDOMClient.createRoot(container);
903 -
904 - await expect(async () => {
905 - await act(() => {
906 - root.render(
907 - <ErrorBoundary>
908 - <BrokenComponentWillMountWithContext />
909 - </ErrorBoundary>,
910 - );
911 - });
912 - }).toErrorDev([
913 - 'Warning: The <BrokenComponentWillMountWithContext /> component appears to be a function component that ' +
914 - 'returns a class instance. ' +
915 - 'Change BrokenComponentWillMountWithContext to a class that extends React.Component instead. ' +
916 - "If you can't use a class try assigning the prototype on the function as a workaround. " +
917 - '`BrokenComponentWillMountWithContext.prototype = React.Component.prototype`. ' +
918 - "Don't use an arrow function since it cannot be called with `new` by React.",
919 - ...gate(flags =>
920 - flags.disableLegacyContext
921 - ? [
922 - 'Warning: BrokenComponentWillMountWithContext uses the legacy childContextTypes API which was removed in React 19. Use React.createContext() instead.',
923 - 'Warning: BrokenComponentWillMountWithContext uses the legacy childContextTypes API which was removed in React 19. Use React.createContext() instead.',
924 - ]
925 - : [],
926 - ),
927 - ]);
928 -
929 - expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
930 - });
931 -
882 it('mounts the error message if mounting fails', async () => {
883 function renderError(error) {
884 return <ErrorMessage message={error.message} />;
packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js
-48
@@ -849,54 +849,6 @@ describe('ReactLegacyErrorBoundaries', () => {
849 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
850 });
851
852 - if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
853 - // @gate !disableLegacyMode
854 - it('renders an error state if module-style context provider throws in componentWillMount', () => {
855 - function BrokenComponentWillMountWithContext() {
856 - return {
857 - getChildContext() {
858 - return {foo: 42};
859 - },
860 - render() {
861 - return <div>{this.props.children}</div>;
862 - },
863 - UNSAFE_componentWillMount() {
864 - throw new Error('Hello');
865 - },
866 - };
867 - }
868 - BrokenComponentWillMountWithContext.childContextTypes = {
869 - foo: PropTypes.number,
870 - };
871 -
872 - const container = document.createElement('div');
873 - expect(() =>
874 - ReactDOM.render(
875 - <ErrorBoundary>
876 - <BrokenComponentWillMountWithContext />
877 - </ErrorBoundary>,
878 - container,
879 - ),
880 - ).toErrorDev([
881 - 'Warning: The <BrokenComponentWillMountWithContext /> component appears to be a function component that ' +
882 - 'returns a class instance. ' +
883 - 'Change BrokenComponentWillMountWithContext to a class that extends React.Component instead. ' +
884 - "If you can't use a class try assigning the prototype on the function as a workaround. " +
885 - '`BrokenComponentWillMountWithContext.prototype = React.Component.prototype`. ' +
886 - "Don't use an arrow function since it cannot be called with `new` by React.",
887 - ...gate(flags =>
888 - flags.disableLegacyContext
889 - ? [
890 - 'Warning: BrokenComponentWillMountWithContext uses the legacy childContextTypes API which was removed in React 19. Use React.createContext() instead.',
891 - 'Warning: BrokenComponentWillMountWithContext uses the legacy childContextTypes API which was removed in React 19. Use React.createContext() instead.',
892 - ]
893 - : [],
894 - ),
895 - ]);
896 - expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
897 - });
898 - }
899 -
852 // @gate !disableLegacyMode
853 it('mounts the error message if mounting fails', () => {
854 function renderError(error) {
packages/react-dom/src/__tests__/refs-test.js
-35
@@ -11,7 +11,6 @@
11
12 let React = require('react');
13 let ReactDOMClient = require('react-dom/client');
14 -let ReactFeatureFlags = require('shared/ReactFeatureFlags');
14 let act = require('internal-test-utils').act;
15
16 // This is testing if string refs are deleted from `instance.refs`
@@ -24,7 +23,6 @@ describe('reactiverefs', () => {
23 jest.resetModules();
24 React = require('react');
25 ReactDOMClient = require('react-dom/client');
27 - ReactFeatureFlags = require('shared/ReactFeatureFlags');
26 act = require('internal-test-utils').act;
27 });
28
@@ -195,38 +193,6 @@ describe('reactiverefs', () => {
193 });
194 });
195
198 -if (!ReactFeatureFlags.disableModulePatternComponents) {
199 - describe('factory components', () => {
200 - it('Should correctly get the ref', async () => {
201 - function Comp() {
202 - return {
203 - elemRef: React.createRef(),
204 - render() {
205 - return <div ref={this.elemRef} />;
206 - },
207 - };
208 - }
209 -
210 - let inst;
211 - await expect(async () => {
212 - const container = document.createElement('div');
213 - const root = ReactDOMClient.createRoot(container);
214 -
215 - await act(() => {
216 - root.render(<Comp ref={current => (inst = current)} />);
217 - });
218 - }).toErrorDev(
219 - 'Warning: The <Comp /> component appears to be a function component that returns a class instance. ' +
220 - 'Change Comp to a class that extends React.Component instead. ' +
221 - "If you can't use a class try assigning the prototype on the function as a workaround. " +
222 - '`Comp.prototype = React.Component.prototype`. ' +
223 - "Don't use an arrow function since it cannot be called with `new` by React.",
224 - );
225 - expect(inst.elemRef.current.tagName).toBe('DIV');
226 - });
227 - });
228 -}
229 -
196 /**
197 * Tests that when a ref hops around children, we can track that correctly.
198 */
@@ -236,7 +202,6 @@ describe('ref swapping', () => {
202 jest.resetModules();
203 React = require('react');
204 ReactDOMClient = require('react-dom/client');
239 - ReactFeatureFlags = require('shared/ReactFeatureFlags');
205 act = require('internal-test-utils').act;
206
207 RefHopsAround = class extends React.Component {
packages/react-reconciler/src/ReactFiber.js
+5 -16
@@ -42,7 +42,6 @@ import {
42 import {NoFlags, Placement, StaticMask} from './ReactFiberFlags';
43 import {ConcurrentRoot} from './ReactRootTags';
44 import {
45 - IndeterminateComponent,
45 ClassComponent,
46 HostRoot,
47 HostComponent,
@@ -248,19 +247,10 @@ export function isSimpleFunctionComponent(type: any): boolean {
247 );
248 }
249
251 -export function resolveLazyComponentTag(Component: Function): WorkTag {
252 - if (typeof Component === 'function') {
253 - return shouldConstruct(Component) ? ClassComponent : FunctionComponent;
254 - } else if (Component !== undefined && Component !== null) {
255 - const $$typeof = Component.$$typeof;
256 - if ($$typeof === REACT_FORWARD_REF_TYPE) {
257 - return ForwardRef;
258 - }
259 - if ($$typeof === REACT_MEMO_TYPE) {
260 - return MemoComponent;
261 - }
262 - }
263 - return IndeterminateComponent;
250 +export function isFunctionClassComponent(
251 + type: (...args: Array<any>) => mixed,
252 +): boolean {
253 + return shouldConstruct(type);
254 }
255
256 // This is used to create an alternate fiber to do work on.
@@ -351,7 +341,6 @@ export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
341 workInProgress._debugInfo = current._debugInfo;
342 workInProgress._debugNeedsRemount = current._debugNeedsRemount;
343 switch (workInProgress.tag) {
354 - case IndeterminateComponent:
344 case FunctionComponent:
345 case SimpleMemoComponent:
346 workInProgress.type = resolveFunctionForHotReloading(current.type);
@@ -492,7 +481,7 @@ export function createFiberFromTypeAndProps(
481 mode: TypeOfMode,
482 lanes: Lanes,
483 ): Fiber {
495 - let fiberTag = IndeterminateComponent;
484 + let fiberTag = FunctionComponent;
485 // The resolved type is set if we know what the final type will be. I.e. it's not lazy.
486 let resolvedType = type;
487 if (typeof type === 'function') {
packages/react-reconciler/src/ReactFiberBeginWork.js
+64 -223
@@ -46,7 +46,6 @@ import {
46 setIsStrictModeForDevtools,
47 } from './ReactFiberDevToolsHook';
48 import {
49 - IndeterminateComponent,
49 FunctionComponent,
50 ClassComponent,
51 HostRoot,
@@ -95,7 +94,6 @@ import ReactSharedInternals from 'shared/ReactSharedInternals';
94 import {
95 debugRenderPhaseSideEffectsForStrictMode,
96 disableLegacyContext,
98 - disableModulePatternComponents,
97 enableProfilerCommitHooks,
98 enableProfilerTimer,
99 enableScopeAPI,
@@ -115,7 +113,12 @@ import shallowEqual from 'shared/shallowEqual';
113 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
114 import getComponentNameFromType from 'shared/getComponentNameFromType';
115 import ReactStrictModeWarnings from './ReactStrictModeWarnings';
118 -import {REACT_LAZY_TYPE, getIteratorFn} from 'shared/ReactSymbols';
116 +import {
117 + REACT_LAZY_TYPE,
118 + REACT_FORWARD_REF_TYPE,
119 + REACT_MEMO_TYPE,
120 + getIteratorFn,
121 +} from 'shared/ReactSymbols';
122 import {
123 getCurrentFiberOwnerNameInDevOrNull,
124 setIsRendering,
@@ -236,7 +239,6 @@ import {
239 queueHydrationError,
240 } from './ReactFiberHydrationContext';
241 import {
239 - adoptClassInstance,
242 constructClassInstance,
243 mountClassInstance,
244 resumeMountClassInstance,
@@ -244,12 +246,12 @@ import {
246 } from './ReactFiberClassComponent';
247 import {resolveDefaultProps} from './ReactFiberLazyComponent';
248 import {
247 - resolveLazyComponentTag,
249 createFiberFromTypeAndProps,
250 createFiberFromFragment,
251 createFiberFromOffscreen,
252 createWorkInProgress,
253 isSimpleFunctionComponent,
254 + isFunctionClassComponent,
255 } from './ReactFiber';
256 import {
257 retryDehydratedSuspenseBoundary,
@@ -305,7 +307,6 @@ export const SelectiveHydrationException: mixed = new Error(
307 let didReceiveUpdate: boolean = false;
308
309 let didWarnAboutBadClass;
308 -let didWarnAboutModulePatternComponent;
310 let didWarnAboutContextTypeOnFunctionComponent;
311 let didWarnAboutGetDerivedStateOnFunctionComponent;
312 let didWarnAboutFunctionRefs;
@@ -316,7 +317,6 @@ let didWarnAboutDefaultPropsOnFunctionComponent;
317
318 if (__DEV__) {
319 didWarnAboutBadClass = ({}: {[string]: boolean});
319 - didWarnAboutModulePatternComponent = ({}: {[string]: boolean});
320 didWarnAboutContextTypeOnFunctionComponent = ({}: {[string]: boolean});
321 didWarnAboutGetDerivedStateOnFunctionComponent = ({}: {[string]: boolean});
322 didWarnAboutFunctionRefs = ({}: {[string]: boolean});
@@ -1053,6 +1053,43 @@ function updateFunctionComponent(
1053 nextProps: any,
1054 renderLanes: Lanes,
1055 ) {
1056 + if (__DEV__) {
1057 + if (
1058 + Component.prototype &&
1059 + typeof Component.prototype.render === 'function'
1060 + ) {
1061 + const componentName = getComponentNameFromType(Component) || 'Unknown';
1062 +
1063 + if (!didWarnAboutBadClass[componentName]) {
1064 + console.error(
1065 + "The <%s /> component appears to have a render method, but doesn't extend React.Component. " +
1066 + 'This is likely to cause errors. Change %s to extend React.Component instead.',
1067 + componentName,
1068 + componentName,
1069 + );
1070 + didWarnAboutBadClass[componentName] = true;
1071 + }
1072 + }
1073 +
1074 + if (workInProgress.mode & StrictLegacyMode) {
1075 + ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null);
1076 + }
1077 +
1078 + if (current === null) {
1079 + // Some validations were previously done in mountIndeterminateComponent however and are now run
1080 + // in updateFuntionComponent but only on mount
1081 + validateFunctionComponentInDev(workInProgress, workInProgress.type);
1082 +
1083 + if (disableLegacyContext && Component.contextTypes) {
1084 + console.error(
1085 + '%s uses the legacy contextTypes API which was removed in React 19. ' +
1086 + 'Use React.createContext() with React.useContext() instead.',
1087 + getComponentNameFromType(Component) || 'Unknown',
1088 + );
1089 + }
1090 + }
1091 + }
1092 +
1093 let context;
1094 if (!disableLegacyContext) {
1095 const unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
@@ -1699,64 +1736,64 @@ function mountLazyComponent(
1736 let Component = init(payload);
1737 // Store the unwrapped component in the type.
1738 workInProgress.type = Component;
1702 - const resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component));
1739 +
1740 const resolvedProps = resolveDefaultProps(Component, props);
1704 - let child;
1705 - switch (resolvedTag) {
1706 - case FunctionComponent: {
1741 + if (typeof Component === 'function') {
1742 + if (isFunctionClassComponent(Component)) {
1743 + workInProgress.tag = ClassComponent;
1744 if (__DEV__) {
1708 - validateFunctionComponentInDev(workInProgress, Component);
1745 workInProgress.type = Component =
1710 - resolveFunctionForHotReloading(Component);
1746 + resolveClassForHotReloading(Component);
1747 }
1712 - child = updateFunctionComponent(
1748 + return updateClassComponent(
1749 null,
1750 workInProgress,
1751 Component,
1752 resolvedProps,
1753 renderLanes,
1754 );
1719 - return child;
1720 - }
1721 - case ClassComponent: {
1755 + } else {
1756 + workInProgress.tag = FunctionComponent;
1757 if (__DEV__) {
1758 + validateFunctionComponentInDev(workInProgress, Component);
1759 workInProgress.type = Component =
1724 - resolveClassForHotReloading(Component);
1760 + resolveFunctionForHotReloading(Component);
1761 }
1726 - child = updateClassComponent(
1762 + return updateFunctionComponent(
1763 null,
1764 workInProgress,
1765 Component,
1766 resolvedProps,
1767 renderLanes,
1768 );
1733 - return child;
1769 }
1735 - case ForwardRef: {
1770 + } else if (Component !== undefined && Component !== null) {
1771 + const $$typeof = Component.$$typeof;
1772 + if ($$typeof === REACT_FORWARD_REF_TYPE) {
1773 + workInProgress.tag = ForwardRef;
1774 if (__DEV__) {
1775 workInProgress.type = Component =
1776 resolveForwardRefForHotReloading(Component);
1777 }
1740 - child = updateForwardRef(
1778 + return updateForwardRef(
1779 null,
1780 workInProgress,
1781 Component,
1782 resolvedProps,
1783 renderLanes,
1784 );
1747 - return child;
1748 - }
1749 - case MemoComponent: {
1750 - child = updateMemoComponent(
1785 + } else if ($$typeof === REACT_MEMO_TYPE) {
1786 + workInProgress.tag = MemoComponent;
1787 + return updateMemoComponent(
1788 null,
1789 workInProgress,
1790 Component,
1791 resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too
1792 renderLanes,
1793 );
1757 - return child;
1794 }
1795 }
1796 +
1797 let hint = '';
1798 if (__DEV__) {
1799 if (
@@ -1816,194 +1853,6 @@ function mountIncompleteClassComponent(
1853 );
1854 }
1855
1819 -function mountIndeterminateComponent(
1820 - _current: null | Fiber,
1821 - workInProgress: Fiber,
1822 - Component: $FlowFixMe,
1823 - renderLanes: Lanes,
1824 -) {
1825 - resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress);
1826 -
1827 - const props = workInProgress.pendingProps;
1828 - let context;
1829 - if (!disableLegacyContext) {
1830 - const unmaskedContext = getUnmaskedContext(
1831 - workInProgress,
1832 - Component,
1833 - false,
1834 - );
1835 - context = getMaskedContext(workInProgress, unmaskedContext);
1836 - }
1837 -
1838 - prepareToReadContext(workInProgress, renderLanes);
1839 - let value;
1840 - let hasId;
1841 -
1842 - if (enableSchedulingProfiler) {
1843 - markComponentRenderStarted(workInProgress);
1844 - }
1845 - if (__DEV__) {
1846 - if (
1847 - Component.prototype &&
1848 - typeof Component.prototype.render === 'function'
1849 - ) {
1850 - const componentName = getComponentNameFromType(Component) || 'Unknown';
1851 -
1852 - if (!didWarnAboutBadClass[componentName]) {
1853 - console.error(
1854 - "The <%s /> component appears to have a render method, but doesn't extend React.Component. " +
1855 - 'This is likely to cause errors. Change %s to extend React.Component instead.',
1856 - componentName,
1857 - componentName,
1858 - );
1859 - didWarnAboutBadClass[componentName] = true;
1860 - }
1861 - }
1862 -
1863 - if (workInProgress.mode & StrictLegacyMode) {
1864 - ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null);
1865 - }
1866 -
1867 - setIsRendering(true);
1868 - ReactCurrentOwner.current = workInProgress;
1869 - value = renderWithHooks(
1870 - null,
1871 - workInProgress,
1872 - Component,
1873 - props,
1874 - context,
1875 - renderLanes,
1876 - );
1877 - hasId = checkDidRenderIdHook();
1878 - setIsRendering(false);
1879 - } else {
1880 - value = renderWithHooks(
1881 - null,
1882 - workInProgress,
1883 - Component,
1884 - props,
1885 - context,
1886 - renderLanes,
1887 - );
1888 - hasId = checkDidRenderIdHook();
1889 - }
1890 - if (enableSchedulingProfiler) {
1891 - markComponentRenderStopped();
1892 - }
1893 -
1894 - // React DevTools reads this flag.
1895 - workInProgress.flags |= PerformedWork;
1896 -
1897 - if (__DEV__) {
1898 - // Support for module components is deprecated and is removed behind a flag.
1899 - // Whether or not it would crash later, we want to show a good message in DEV first.
1900 - if (
1901 - typeof value === 'object' &&
1902 - value !== null &&
1903 - typeof value.render === 'function' &&
1904 - value.$$typeof === undefined
1905 - ) {
1906 - const componentName = getComponentNameFromType(Component) || 'Unknown';
1907 - if (!didWarnAboutModulePatternComponent[componentName]) {
1908 - console.error(
1909 - 'The <%s /> component appears to be a function component that returns a class instance. ' +
1910 - 'Change %s to a class that extends React.Component instead. ' +
1911 - "If you can't use a class try assigning the prototype on the function as a workaround. " +
1912 - "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " +
1913 - 'cannot be called with `new` by React.',
1914 - componentName,
1915 - componentName,
1916 - componentName,
1917 - );
1918 - didWarnAboutModulePatternComponent[componentName] = true;
1919 - }
1920 - }
1921 - }
1922 -
1923 - if (
1924 - // Run these checks in production only if the flag is off.
1925 - // Eventually we'll delete this branch altogether.
1926 - !disableModulePatternComponents &&
1927 - typeof value === 'object' &&
1928 - value !== null &&
1929 - typeof value.render === 'function' &&
1930 - value.$$typeof === undefined
1931 - ) {
1932 - if (__DEV__) {
1933 - const componentName = getComponentNameFromType(Component) || 'Unknown';
1934 - if (!didWarnAboutModulePatternComponent[componentName]) {
1935 - console.error(
1936 - 'The <%s /> component appears to be a function component that returns a class instance. ' +
1937 - 'Change %s to a class that extends React.Component instead. ' +
1938 - "If you can't use a class try assigning the prototype on the function as a workaround. " +
1939 - "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " +
1940 - 'cannot be called with `new` by React.',
1941 - componentName,
1942 - componentName,
1943 - componentName,
1944 - );
1945 - didWarnAboutModulePatternComponent[componentName] = true;
1946 - }
1947 - }
1948 -
1949 - // Proceed under the assumption that this is a class instance
1950 - workInProgress.tag = ClassComponent;
1951 -
1952 - // Throw out any hooks that were used.
1953 - workInProgress.memoizedState = null;
1954 - workInProgress.updateQueue = null;
1955 -
1956 - // Push context providers early to prevent context stack mismatches.
1957 - // During mounting we don't know the child context yet as the instance doesn't exist.
1958 - // We will invalidate the child context in finishClassComponent() right after rendering.
1959 - let hasContext = false;
1960 - if (isLegacyContextProvider(Component)) {
1961 - hasContext = true;
1962 - pushLegacyContextProvider(workInProgress);
1963 - } else {
1964 - hasContext = false;
1965 - }
1966 -
1967 - workInProgress.memoizedState =
1968 - value.state !== null && value.state !== undefined ? value.state : null;
1969 -
1970 - initializeUpdateQueue(workInProgress);
1971 -
1972 - adoptClassInstance(workInProgress, value);
1973 - mountClassInstance(workInProgress, Component, props, renderLanes);
1974 - return finishClassComponent(
1975 - null,
1976 - workInProgress,
1977 - Component,
1978 - true,
1979 - hasContext,
1980 - renderLanes,
1981 - );
1982 - } else {
1983 - // Proceed under the assumption that this is a function component
1984 - workInProgress.tag = FunctionComponent;
1985 - if (__DEV__) {
1986 - if (disableLegacyContext && Component.contextTypes) {
1987 - console.error(
1988 - '%s uses the legacy contextTypes API which was removed in React 19. ' +
1989 - 'Use React.createContext() with React.useContext() instead.',
1990 - getComponentNameFromType(Component) || 'Unknown',
1991 - );
1992 - }
1993 - }
1994 -
1995 - if (getIsHydrating() && hasId) {
1996 - pushMaterializedTreeId(workInProgress);
1997 - }
1998 -
1999 - reconcileChildren(null, workInProgress, value, renderLanes);
2000 - if (__DEV__) {
2001 - validateFunctionComponentInDev(workInProgress, Component);
2002 - }
2003 - return workInProgress.child;
2004 - }
2005 -}
2006 -
1856 function validateFunctionComponentInDev(workInProgress: Fiber, Component: any) {
1857 if (__DEV__) {
1858 if (Component) {
@@ -4027,14 +3876,6 @@ function beginWork(
3876 workInProgress.lanes = NoLanes;
3877
3878 switch (workInProgress.tag) {
4030 - case IndeterminateComponent: {
4031 - return mountIndeterminateComponent(
4032 - current,
4033 - workInProgress,
4034 - workInProgress.type,
4035 - renderLanes,
4036 - );
4037 - }
3879 case LazyComponent: {
3880 const elementType = workInProgress.elementType;
3881 return mountLazyComponent(
packages/react-reconciler/src/ReactFiberClassComponent.js
+7 -12
@@ -569,16 +569,6 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) {
569 }
570 }
571
572 -function adoptClassInstance(workInProgress: Fiber, instance: any): void {
573 - instance.updater = classComponentUpdater;
574 - workInProgress.stateNode = instance;
575 - // The instance needs access to the fiber so that it can schedule updates
576 - setInstance(instance, workInProgress);
577 - if (__DEV__) {
578 - instance._reactInternalInstance = fakeInternalInstance;
579 - }
580 -}
581 -
572 function constructClassInstance(
573 workInProgress: Fiber,
574 ctor: any,
@@ -659,7 +649,13 @@ function constructClassInstance(
649 instance.state !== null && instance.state !== undefined
650 ? instance.state
651 : null);
662 - adoptClassInstance(workInProgress, instance);
652 + instance.updater = classComponentUpdater;
653 + workInProgress.stateNode = instance;
654 + // The instance needs access to the fiber so that it can schedule updates
655 + setInstance(instance, workInProgress);
656 + if (__DEV__) {
657 + instance._reactInternalInstance = fakeInternalInstance;
658 + }
659
660 if (__DEV__) {
661 if (typeof ctor.getDerivedStateFromProps === 'function' && state === null) {
@@ -1230,7 +1226,6 @@ function updateClassInstance(
1226 }
1227
1228 export {
1233 - adoptClassInstance,
1229 constructClassInstance,
1230 mountClassInstance,
1231 resumeMountClassInstance,
packages/react-reconciler/src/ReactFiberCompleteWork.js
-2
@@ -45,7 +45,6 @@ import {
45 import {now} from './Scheduler';
46
47 import {
48 - IndeterminateComponent,
48 FunctionComponent,
49 ClassComponent,
50 HostRoot,
@@ -949,7 +948,6 @@ function completeWork(
948 // for hydration.
949 popTreeContext(workInProgress);
950 switch (workInProgress.tag) {
952 - case IndeterminateComponent:
951 case LazyComponent:
952 case SimpleMemoComponent:
953 case FunctionComponent:
packages/react-reconciler/src/ReactFiberComponentStack.js
-2
@@ -17,7 +17,6 @@ import {
17 SuspenseComponent,
18 SuspenseListComponent,
19 FunctionComponent,
20 - IndeterminateComponent,
20 ForwardRef,
21 SimpleMemoComponent,
22 ClassComponent,
@@ -47,7 +46,6 @@ function describeFiber(fiber: Fiber): string {
46 case SuspenseListComponent:
47 return describeBuiltInComponentFrame('SuspenseList', owner);
48 case FunctionComponent:
50 - case IndeterminateComponent:
49 case SimpleMemoComponent:
50 return describeFunctionComponentFrame(fiber.type, owner);
51 case ForwardRef:
packages/react-reconciler/src/ReactFiberHydrationDiffs.js
-2
@@ -17,7 +17,6 @@ import {
17 SuspenseComponent,
18 SuspenseListComponent,
19 FunctionComponent,
20 - IndeterminateComponent,
20 ForwardRef,
21 SimpleMemoComponent,
22 ClassComponent,
@@ -87,7 +86,6 @@ function describeFiberType(fiber: Fiber): null | string {
86 case SuspenseListComponent:
87 return 'SuspenseList';
88 case FunctionComponent:
90 - case IndeterminateComponent:
89 case SimpleMemoComponent:
90 const fn = fiber.type;
91 return fn.displayName || fn.name || null;
packages/react-reconciler/src/ReactFiberWorkLoop.js
-8
@@ -90,7 +90,6 @@ import {
90 } from './ReactTypeOfMode';
91 import {
92 HostRoot,
93 - IndeterminateComponent,
93 ClassComponent,
94 SuspenseComponent,
95 SuspenseListComponent,
@@ -2395,12 +2394,6 @@ function replaySuspendedUnitOfWork(unitOfWork: Fiber): void {
2394 startProfilerTimer(unitOfWork);
2395 }
2396 switch (unitOfWork.tag) {
2398 - case IndeterminateComponent: {
2399 - // Because it suspended with `use`, we can assume it's a
2400 - // function component.
2401 - unitOfWork.tag = FunctionComponent;
2402 - // Fallthrough to the next branch.
2403 - }
2397 case SimpleMemoComponent:
2398 case FunctionComponent: {
2399 // Resolve `defaultProps`. This logic is copied from `beginWork`.
@@ -3823,7 +3816,6 @@ export function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber: Fiber) {
3816
3817 const tag = fiber.tag;
3818 if (
3826 - tag !== IndeterminateComponent &&
3819 tag !== HostRoot &&
3820 tag !== ClassComponent &&
3821 tag !== FunctionComponent &&
packages/react-reconciler/src/ReactWorkTags.js
-1
@@ -39,7 +39,6 @@ export type WorkTag =
39
40 export const FunctionComponent = 0;
41 export const ClassComponent = 1;
42 -export const IndeterminateComponent = 2; // Before we know whether it is function or class
42 export const HostRoot = 3; // Root of a host tree. Could be nested inside another node.
43 export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
44 export const HostComponent = 5;
packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js
-50
@@ -1308,16 +1308,6 @@ describe('ReactHooks', () => {
1308 return <div />;
1309 });
1310
1311 - function Factory() {
1312 - return {
1313 - state: {},
1314 - render() {
1315 - renderCount++;
1316 - return <div />;
1317 - },
1318 - };
1319 - }
1320 -
1311 let renderer;
1312 await act(() => {
1313 renderer = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
@@ -1410,46 +1400,6 @@ describe('ReactHooks', () => {
1400 });
1401 expect(renderCount).toBe(__DEV__ ? 2 : 1);
1402
1413 - if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
1414 - renderCount = 0;
1415 - await expect(async () => {
1416 - await act(() => {
1417 - renderer.update(<Factory />);
1418 - });
1419 - }).toErrorDev(
1420 - 'Warning: The <Factory /> component appears to be a function component that returns a class instance. ' +
1421 - 'Change Factory to a class that extends React.Component instead. ' +
1422 - "If you can't use a class try assigning the prototype on the function as a workaround. " +
1423 - '`Factory.prototype = React.Component.prototype`. ' +
1424 - "Don't use an arrow function since it cannot be called with `new` by React.",
1425 - );
1426 - expect(renderCount).toBe(1);
1427 - renderCount = 0;
1428 - await act(() => {
1429 - renderer.update(<Factory />);
1430 - });
1431 - expect(renderCount).toBe(1);
1432 -
1433 - renderCount = 0;
1434 - await act(() => {
1435 - renderer.update(
1436 - <StrictMode>
1437 - <Factory />
1438 - </StrictMode>,
1439 - );
1440 - });
1441 - expect(renderCount).toBe(__DEV__ ? 2 : 1); // Treated like a class
1442 - renderCount = 0;
1443 - await act(() => {
1444 - renderer.update(
1445 - <StrictMode>
1446 - <Factory />
1447 - </StrictMode>,
1448 - );
1449 - });
1450 - expect(renderCount).toBe(__DEV__ ? 2 : 1); // Treated like a class
1451 - }
1452 -
1403 renderCount = 0;
1404 await act(() => {
1405 renderer.update(<HasHooks />);
packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
-38
@@ -227,44 +227,6 @@ describe('ReactHooksWithNoopRenderer', () => {
227 await waitForAll([10]);
228 });
229
230 - // @gate !disableModulePatternComponents
231 - it('throws inside module-style components', async () => {
232 - function Counter() {
233 - return {
234 - render() {
235 - const [count] = useState(0);
236 - return <Text text={this.props.label + ': ' + count} />;
237 - },
238 - };
239 - }
240 - ReactNoop.render(<Counter />);
241 - await expect(
242 - async () =>
243 - await waitForThrow(
244 - 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen ' +
245 - 'for one of the following reasons:\n' +
246 - '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
247 - '2. You might be breaking the Rules of Hooks\n' +
248 - '3. You might have more than one copy of React in the same app\n' +
249 - 'See https://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem.',
250 - ),
251 - ).toErrorDev(
252 - 'Warning: The <Counter /> component appears to be a function component that returns a class instance. ' +
253 - 'Change Counter to a class that extends React.Component instead. ' +
254 - "If you can't use a class try assigning the prototype on the function as a workaround. " +
255 - '`Counter.prototype = React.Component.prototype`. ' +
256 - "Don't use an arrow function since it cannot be called with `new` by React.",
257 - );
258 -
259 - // Confirm that a subsequent hook works properly.
260 - function GoodCounter(props) {
261 - const [count] = useState(props.initialCount);
262 - return <Text text={count} />;
263 - }
264 - ReactNoop.render(<GoodCounter initialCount={10} />);
265 - await waitForAll([10]);
266 - });
267 -
230 it('throws when called outside the render phase', async () => {
231 expect(() => {
232 expect(() => useState(0)).toThrow(
packages/react-reconciler/src/__tests__/ReactIncremental-test.js
-42
@@ -1864,48 +1864,6 @@ describe('ReactIncremental', () => {
1864 ]);
1865 });
1866
1867 - // @gate !disableModulePatternComponents
1868 - // @gate !disableLegacyContext
1869 - it('does not leak own context into context provider (factory components)', async () => {
1870 - function Recurse(props, context) {
1871 - return {
1872 - getChildContext() {
1873 - return {n: (context.n || 3) - 1};
1874 - },
1875 - render() {
1876 - Scheduler.log('Recurse ' + JSON.stringify(context));
1877 - if (context.n === 0) {
1878 - return null;
1879 - }
1880 - return <Recurse />;
1881 - },
1882 - };
1883 - }
1884 - Recurse.contextTypes = {
1885 - n: PropTypes.number,
1886 - };
1887 - Recurse.childContextTypes = {
1888 - n: PropTypes.number,
1889 - };
1890 -
1891 - ReactNoop.render(<Recurse />);
1892 - await expect(
1893 - async () =>
1894 - await waitForAll([
1895 - 'Recurse {}',
1896 - 'Recurse {"n":2}',
1897 - 'Recurse {"n":1}',
1898 - 'Recurse {"n":0}',
1899 - ]),
1900 - ).toErrorDev([
1901 - 'Warning: The <Recurse /> component appears to be a function component that returns a class instance. ' +
1902 - 'Change Recurse to a class that extends React.Component instead. ' +
1903 - "If you can't use a class try assigning the prototype on the function as a workaround. " +
1904 - '`Recurse.prototype = React.Component.prototype`. ' +
1905 - "Don't use an arrow function since it cannot be called with `new` by React.",
1906 - ]);
1907 - });
1908 -
1867 // @gate www
1868 // @gate !disableLegacyContext
1869 it('provides context when reusing work', async () => {
packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
-39
@@ -1754,45 +1754,6 @@ describe('ReactIncrementalErrorHandling', () => {
1754 );
1755 });
1756
1757 - // @gate !disableModulePatternComponents
1758 - it('handles error thrown inside getDerivedStateFromProps of a module-style context provider', async () => {
1759 - function Provider() {
1760 - return {
1761 - getChildContext() {
1762 - return {foo: 'bar'};
1763 - },
1764 - render() {
1765 - return 'Hi';
1766 - },
1767 - };
1768 - }
1769 - Provider.childContextTypes = {
1770 - x: () => {},
1771 - };
1772 - Provider.getDerivedStateFromProps = () => {
1773 - throw new Error('Oops!');
1774 - };
1775 -
1776 - ReactNoop.render(<Provider />);
1777 - await expect(async () => {
1778 - await waitForThrow('Oops!');
1779 - }).toErrorDev([
1780 - 'Warning: The <Provider /> component appears to be a function component that returns a class instance. ' +
1781 - 'Change Provider to a class that extends React.Component instead. ' +
1782 - "If you can't use a class try assigning the prototype on the function as a workaround. " +
1783 - '`Provider.prototype = React.Component.prototype`. ' +
1784 - "Don't use an arrow function since it cannot be called with `new` by React.",
1785 - ...gate(flags =>
1786 - flags.disableLegacyContext
1787 - ? [
1788 - 'Warning: Provider uses the legacy childContextTypes API which was removed in React 19. Use React.createContext() instead.',
1789 - 'Warning: Provider uses the legacy childContextTypes API which was removed in React 19. Use React.createContext() instead.',
1790 - ]
1791 - : [],
1792 - ),
1793 - ]);
1794 - });
1795 -
1757 it('uncaught errors should be discarded if the render is aborted', async () => {
1758 const root = ReactNoop.createRoot();
1759
packages/react-reconciler/src/__tests__/ReactSubtreeFlagsWarning-test.js
+2 -6
@@ -132,11 +132,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
132
133 // @gate experimental || www
134 it('regression: false positive for legacy suspense', async () => {
135 - // Wrapping in memo because regular function components go through the
136 - // mountIndeterminateComponent path, which acts like there's no `current`
137 - // fiber even though there is. `memo` is not indeterminate, so it goes
138 - // through the update path.
139 - const Child = React.memo(({text}) => {
135 + const Child = ({text}) => {
136 // If text hasn't resolved, this will throw and exit before the passive
137 // static effect flag is added by the useEffect call below.
138 readText(text);
@@ -147,7 +143,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
143
144 Scheduler.log(text);
145 return text;
150 - });
146 + };
147
148 function App() {
149 return (
packages/react-reconciler/src/getComponentNameFromFiber.js
-2
@@ -18,7 +18,6 @@ import {
18 import {
19 FunctionComponent,
20 ClassComponent,
21 - IndeterminateComponent,
21 HostRoot,
22 HostPortal,
23 HostComponent,
@@ -128,7 +127,6 @@ export default function getComponentNameFromFiber(fiber: Fiber): string | null {
127 case ClassComponent:
128 case FunctionComponent:
129 case IncompleteClassComponent:
131 - case IndeterminateComponent:
130 case MemoComponent:
131 case SimpleMemoComponent:
132 if (typeof type === 'function') {
packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js
-48
@@ -1639,54 +1639,6 @@ describe('ReactFreshIntegration', () => {
1639 }
1640 });
1641
1642 - if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
1643 - it('remounts deprecated factory components', async () => {
1644 - if (__DEV__) {
1645 - await expect(async () => {
1646 - await render(`
1647 - function Parent() {
1648 - return {
1649 - render() {
1650 - return <Child prop="A" />;
1651 - }
1652 - };
1653 - };
1654 -
1655 - function Child({prop}) {
1656 - return <h1>{prop}1</h1>;
1657 - };
1658 -
1659 - export default Parent;
1660 - `);
1661 - }).toErrorDev(
1662 - 'The <Parent /> component appears to be a function component ' +
1663 - 'that returns a class instance.',
1664 - );
1665 - const el = container.firstChild;
1666 - expect(el.textContent).toBe('A1');
1667 - await patch(`
1668 - function Parent() {
1669 - return {
1670 - render() {
1671 - return <Child prop="B" />;
1672 - }
1673 - };
1674 - };
1675 -
1676 - function Child({prop}) {
1677 - return <h1>{prop}2</h1>;
1678 - };
1679 -
1680 - export default Parent;
1681 - `);
1682 - // Like classes, factory components always remount.
1683 - expect(container.firstChild).not.toBe(el);
1684 - const newEl = container.firstChild;
1685 - expect(newEl.textContent).toBe('B2');
1686 - }
1687 - });
1688 - }
1689 -
1642 describe('with inline requires', () => {
1643 beforeEach(() => {
1644 global.FakeModuleSystem = {};
packages/react-server/src/ReactFizzServer.js
+19 -80
@@ -137,7 +137,6 @@ import {
137 import ReactSharedInternals from 'shared/ReactSharedInternals';
138 import {
139 disableLegacyContext,
140 - disableModulePatternComponents,
140 enableBigIntSupport,
141 enableScopeAPI,
142 enableSuspenseAvoidThisFallbackFizz,
@@ -1388,7 +1387,6 @@ function renderClassComponent(
1387 }
1388
1389 const didWarnAboutBadClass: {[string]: boolean} = {};
1391 -const didWarnAboutModulePatternComponent: {[string]: boolean} = {};
1390 const didWarnAboutContextTypeOnFunctionComponent: {[string]: boolean} = {};
1391 const didWarnAboutGetDerivedStateOnFunctionComponent: {[string]: boolean} = {};
1392 let didWarnAboutReassigningProps = false;
@@ -1396,9 +1394,7 @@ const didWarnAboutDefaultPropsOnFunctionComponent: {[string]: boolean} = {};
1394 let didWarnAboutGenerators = false;
1395 let didWarnAboutMaps = false;
1396
1399 -// This would typically be a function component but we still support module pattern
1400 -// components for some reason.
1401 -function renderIndeterminateComponent(
1397 +function renderFunctionComponent(
1398 request: Request,
1399 task: Task,
1400 keyPath: KeyNode,
@@ -1444,83 +1440,26 @@ function renderIndeterminateComponent(
1440 const actionStateMatchingIndex = getActionStateMatchingIndex();
1441
1442 if (__DEV__) {
1447 - // Support for module components is deprecated and is removed behind a flag.
1448 - // Whether or not it would crash later, we want to show a good message in DEV first.
1449 - if (
1450 - typeof value === 'object' &&
1451 - value !== null &&
1452 - typeof value.render === 'function' &&
1453 - value.$$typeof === undefined
1454 - ) {
1455 - const componentName = getComponentNameFromType(Component) || 'Unknown';
1456 - if (!didWarnAboutModulePatternComponent[componentName]) {
1457 - console.error(
1458 - 'The <%s /> component appears to be a function component that returns a class instance. ' +
1459 - 'Change %s to a class that extends React.Component instead. ' +
1460 - "If you can't use a class try assigning the prototype on the function as a workaround. " +
1461 - "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " +
1462 - 'cannot be called with `new` by React.',
1463 - componentName,
1464 - componentName,
1465 - componentName,
1466 - );
1467 - didWarnAboutModulePatternComponent[componentName] = true;
1468 - }
1443 + if (disableLegacyContext && Component.contextTypes) {
1444 + console.error(
1445 + '%s uses the legacy contextTypes API which was removed in React 19. ' +
1446 + 'Use React.createContext() with React.useContext() instead.',
1447 + getComponentNameFromType(Component) || 'Unknown',
1448 + );
1449 }
1450 }
1471 -
1472 - if (
1473 - // Run these checks in production only if the flag is off.
1474 - // Eventually we'll delete this branch altogether.
1475 - !disableModulePatternComponents &&
1476 - typeof value === 'object' &&
1477 - value !== null &&
1478 - typeof value.render === 'function' &&
1479 - value.$$typeof === undefined
1480 - ) {
1481 - if (__DEV__) {
1482 - const componentName = getComponentNameFromType(Component) || 'Unknown';
1483 - if (!didWarnAboutModulePatternComponent[componentName]) {
1484 - console.error(
1485 - 'The <%s /> component appears to be a function component that returns a class instance. ' +
1486 - 'Change %s to a class that extends React.Component instead. ' +
1487 - "If you can't use a class try assigning the prototype on the function as a workaround. " +
1488 - "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " +
1489 - 'cannot be called with `new` by React.',
1490 - componentName,
1491 - componentName,
1492 - componentName,
1493 - );
1494 - didWarnAboutModulePatternComponent[componentName] = true;
1495 - }
1496 - }
1497 -
1498 - mountClassInstance(value, Component, props, legacyContext);
1499 - finishClassComponent(request, task, keyPath, value, Component, props);
1500 - } else {
1501 - // Proceed under the assumption that this is a function component
1502 - if (__DEV__) {
1503 - if (disableLegacyContext && Component.contextTypes) {
1504 - console.error(
1505 - '%s uses the legacy contextTypes API which was removed in React 19. ' +
1506 - 'Use React.createContext() with React.useContext() instead.',
1507 - getComponentNameFromType(Component) || 'Unknown',
1508 - );
1509 - }
1510 - }
1511 - if (__DEV__) {
1512 - validateFunctionComponentInDev(Component);
1513 - }
1514 - finishFunctionComponent(
1515 - request,
1516 - task,
1517 - keyPath,
1518 - value,
1519 - hasId,
1520 - actionStateCount,
1521 - actionStateMatchingIndex,
1522 - );
1451 + if (__DEV__) {
1452 + validateFunctionComponentInDev(Component);
1453 }
1454 + finishFunctionComponent(
1455 + request,
1456 + task,
1457 + keyPath,
1458 + value,
1459 + hasId,
1460 + actionStateCount,
1461 + actionStateMatchingIndex,
1462 + );
1463 task.componentStack = previousComponentStack;
1464 }
1465
@@ -1825,7 +1764,7 @@ function renderElement(
1764 renderClassComponent(request, task, keyPath, type, props);
1765 return;
1766 } else {
1828 - renderIndeterminateComponent(request, task, keyPath, type, props);
1767 + renderFunctionComponent(request, task, keyPath, type, props);
1768 return;
1769 }
1770 }
packages/shared/ReactFeatureFlags.js
-2
@@ -206,8 +206,6 @@ export const enableRenderableContext = __NEXT_MAJOR__;
206 // when we plan to enable them.
207 // -----------------------------------------------------------------------------
208
209 -export const disableModulePatternComponents = __NEXT_MAJOR__;
210 -
209 export const enableUseRefAccessWarning = false;
210
211 // Enables time slicing for updates that aren't wrapped in startTransition.
packages/shared/forks/ReactFeatureFlags.native-fb.js
-1
@@ -34,7 +34,6 @@ export const {
34 } = dynamicFlags;
35
36 // The rest of the flags are static for better dead code elimination.
37 -export const disableModulePatternComponents = true;
37 export const enableDebugTracing = false;
38 export const enableAsyncDebugInfo = false;
39 export const enableSchedulingProfiler = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.native-oss.js
-1
@@ -31,7 +31,6 @@ export const enableDeferRootSchedulingToMicrotask = __TODO_NEXT_RN_MAJOR__;
31 export const alwaysThrottleRetries = __TODO_NEXT_RN_MAJOR__;
32 export const enableInfiniteRenderLoopDetection = __TODO_NEXT_RN_MAJOR__;
33 export const enableComponentStackLocations = __TODO_NEXT_RN_MAJOR__;
34 -export const disableModulePatternComponents = __TODO_NEXT_RN_MAJOR__;
34
35 // -----------------------------------------------------------------------------
36 // These are ready to flip after the next React npm release (or RN switches to
packages/shared/forks/ReactFeatureFlags.test-renderer.js
-1
@@ -94,7 +94,6 @@ export const disableLegacyMode = __NEXT_MAJOR__;
94 export const disableLegacyContext = __NEXT_MAJOR__;
95 export const disableDOMTestUtils = __NEXT_MAJOR__;
96 export const enableNewBooleanProps = __NEXT_MAJOR__;
97 -export const disableModulePatternComponents = __NEXT_MAJOR__;
97 export const enableRenderableContext = __NEXT_MAJOR__;
98 export const enableReactTestRendererWarning = __NEXT_MAJOR__;
99
packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
-1
@@ -34,7 +34,6 @@ export const enableSuspenseCallback = false;
34 export const disableLegacyContext = false;
35 export const enableTrustedTypesIntegration = false;
36 export const disableTextareaChildren = false;
37 -export const disableModulePatternComponents = true;
37 export const enableComponentStackLocations = false;
38 export const enableLegacyFBSupport = false;
39 export const enableFilterEmptyStringAttributesDOM = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
-1
@@ -34,7 +34,6 @@ export const enableSuspenseCallback = true;
34 export const disableLegacyContext = false;
35 export const enableTrustedTypesIntegration = false;
36 export const disableTextareaChildren = false;
37 -export const disableModulePatternComponents = true;
37 export const enableSuspenseAvoidThisFallback = true;
38 export const enableSuspenseAvoidThisFallbackFizz = false;
39 export const enableCPUSuspense = false;
packages/shared/forks/ReactFeatureFlags.www.js
-2
@@ -82,8 +82,6 @@ export const enablePostpone = false;
82 // Need to remove it.
83 export const disableCommentsAsDOMContainers = false;
84
85 -export const disableModulePatternComponents = true;
86 -
85 export const enableCreateEventHandleAPI = true;
86
87 export const enableScopeAPI = true;