@samitouri / QOS-React / commits / a73c3450e1

Remove module pattern function component support (flag only) (#28671)

Remove module pattern function component support (flag only) > This is a redo of #27742, but only including the flag removal, excluding further simplifications. 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.

Jan Kassens committed Mar 29, 2024 at 11:16 UTC a73c3450e1b528fa6cb3e94fa4d4359c7a4b61f1
22 files changed +67 -697
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
+23 -52
@@ -211,64 +211,35 @@ 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 - }
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
224 - const el = document.createElement('div');
225 - const root = ReactDOMClient.createRoot(el);
223 + const el = document.createElement('div');
224 + const root = ReactDOMClient.createRoot(el);
225 + await expect(async () => {
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 - }
253 -
254 - const el = document.createElement('div');
255 - const root = ReactDOMClient.createRoot(el);
256 - expect(() => {
257 - ReactDOM.flushSync(() => {
227 + await act(() => {
228 root.render(<Child test="test" />);
229 });
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.",
230 + }).rejects.toThrow(
231 + 'Objects are not valid as a React child (found: object with keys {render}).',
232 );
233 + }).toErrorDev(
234 + 'Warning: The <Child /> component appears to be a function component that returns a class instance. ' +
235 + 'Change Child to a class that extends React.Component instead. ' +
236 + "If you can't use a class try assigning the prototype on the function as a workaround. " +
237 + '`Child.prototype = React.Component.prototype`. ' +
238 + "Don't use an arrow function since it cannot be called with `new` by React.",
239 + );
240
268 - expect(el.textContent).toBe('test');
269 - });
270 - }
271 -
241 + expect(el.textContent).toBe('');
242 + });
243 it('should use default values for undefined props', async () => {
244 class Component extends React.Component {
245 static defaultProps = {prop: 'testKey'};
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/ReactFiberBeginWork.js
+17 -80
@@ -95,7 +95,6 @@ import ReactSharedInternals from 'shared/ReactSharedInternals';
95 import {
96 debugRenderPhaseSideEffectsForStrictMode,
97 disableLegacyContext,
98 - disableModulePatternComponents,
98 enableProfilerCommitHooks,
99 enableProfilerTimer,
100 enableScopeAPI,
@@ -236,7 +235,6 @@ import {
235 queueHydrationError,
236 } from './ReactFiberHydrationContext';
237 import {
239 - adoptClassInstance,
238 constructClassInstance,
239 mountClassInstance,
240 resumeMountClassInstance,
@@ -1920,88 +1918,27 @@ function mountIndeterminateComponent(
1918 }
1919 }
1920
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 - }
1921 + // Proceed under the assumption that this is a function component
1922 + workInProgress.tag = FunctionComponent;
1923 + if (__DEV__) {
1924 + if (disableLegacyContext && Component.contextTypes) {
1925 + console.error(
1926 + '%s uses the legacy contextTypes API which was removed in React 19. ' +
1927 + 'Use React.createContext() with React.useContext() instead.',
1928 + getComponentNameFromType(Component) || 'Unknown',
1929 + );
1930 }
1931 + }
1932
1995 - if (getIsHydrating() && hasId) {
1996 - pushMaterializedTreeId(workInProgress);
1997 - }
1933 + if (getIsHydrating() && hasId) {
1934 + pushMaterializedTreeId(workInProgress);
1935 + }
1936
1999 - reconcileChildren(null, workInProgress, value, renderLanes);
2000 - if (__DEV__) {
2001 - validateFunctionComponentInDev(workInProgress, Component);
2002 - }
2003 - return workInProgress.child;
1937 + reconcileChildren(null, workInProgress, value, renderLanes);
1938 + if (__DEV__) {
1939 + validateFunctionComponentInDev(workInProgress, Component);
1940 }
1941 + return workInProgress.child;
1942 }
1943
1944 function validateFunctionComponentInDev(workInProgress: Fiber, Component: any) {
packages/react-reconciler/src/ReactFiberClassComponent.js
-1
@@ -1230,7 +1230,6 @@ function updateClassInstance(
1230 }
1231
1232 export {
1233 - adoptClassInstance,
1233 constructClassInstance,
1234 mountClassInstance,
1235 resumeMountClassInstance,
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-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
+20 -51
@@ -137,7 +137,6 @@ import {
137 import ReactSharedInternals from 'shared/ReactSharedInternals';
138 import {
139 disableLegacyContext,
140 - disableModulePatternComponents,
140 enableBigIntSupport,
141 enableScopeAPI,
142 enableSuspenseAvoidThisFallbackFizz,
@@ -1469,58 +1468,28 @@ function renderIndeterminateComponent(
1468 }
1469 }
1470
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);
1471 + // Proceed under the assumption that this is a function component
1472 + if (__DEV__) {
1473 + if (disableLegacyContext && Component.contextTypes) {
1474 + console.error(
1475 + '%s uses the legacy contextTypes API which was removed in React 19. ' +
1476 + 'Use React.createContext() with React.useContext() instead.',
1477 + getComponentNameFromType(Component) || 'Unknown',
1478 + );
1479 }
1514 - finishFunctionComponent(
1515 - request,
1516 - task,
1517 - keyPath,
1518 - value,
1519 - hasId,
1520 - actionStateCount,
1521 - actionStateMatchingIndex,
1522 - );
1480 }
1481 + if (__DEV__) {
1482 + validateFunctionComponentInDev(Component);
1483 + }
1484 + finishFunctionComponent(
1485 + request,
1486 + task,
1487 + keyPath,
1488 + value,
1489 + hasId,
1490 + actionStateCount,
1491 + actionStateMatchingIndex,
1492 + );
1493 task.componentStack = previousComponentStack;
1494 }
1495
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;