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;