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;