@samitouri / QOS-React-2 / commits / 5998a77519

Reland #28672: Remove IndeterminateComponent (#28681)

This PR relands #28672 on top of the flag removal and the test demonstrating a breakage in Suspense for legacy mode. React has deprecated module pattern Function Components for many years at this point. Supporting this pattern required React to have a concept of an indeterminate component so that when a component first renders it can turn into either a ClassComponent or a FunctionComponent depending on what it returns. While this feature was deprecated and put behind a flag it is still in stable. This change remvoes the flag, removes the warnings, and removes the concept of IndeterminateComponent from the React codebase. While removing IndeterminateComponent type Seb and I discovered that we needed a concept of IncompleteFunctionComponent to support Suspense in legacy mode. This new work tag is only needed as long as legacy mode is around and ideally any code that considers this tag will be excludable from OSS builds once we land extra gates using `disableLegacyMode` flag.

Josh Story committed Apr 2, 2024 at 17:42 UTC 5998a775194f491afa5d3badd9afe9ceaf12845e
15 files changed +149 -257
packages/react-devtools-shared/src/backend/renderer.js
+13 -1
@@ -225,7 +225,8 @@ export function getInternalReactConstants(version: string): {
225 HostSingleton: 27, // Same as above
226 HostText: 6,
227 IncompleteClassComponent: 17,
228 - IndeterminateComponent: 2,
228 + IncompleteFunctionComponent: 28,
229 + IndeterminateComponent: 2, // removed in 19.0.0
230 LazyComponent: 16,
231 LegacyHiddenComponent: 23,
232 MemoComponent: 14,
@@ -259,6 +260,7 @@ export function getInternalReactConstants(version: string): {
260 HostSingleton: -1, // Doesn't exist yet
261 HostText: 6,
262 IncompleteClassComponent: 17,
263 + IncompleteFunctionComponent: -1, // Doesn't exist yet
264 IndeterminateComponent: 2,
265 LazyComponent: 16,
266 LegacyHiddenComponent: 24,
@@ -292,6 +294,7 @@ export function getInternalReactConstants(version: string): {
294 HostSingleton: -1, // Doesn't exist yet
295 HostText: 6,
296 IncompleteClassComponent: 17,
297 + IncompleteFunctionComponent: -1, // Doesn't exist yet
298 IndeterminateComponent: 2,
299 LazyComponent: 16,
300 LegacyHiddenComponent: -1,
@@ -325,6 +328,7 @@ export function getInternalReactConstants(version: string): {
328 HostSingleton: -1, // Doesn't exist yet
329 HostText: 8,
330 IncompleteClassComponent: -1, // Doesn't exist yet
331 + IncompleteFunctionComponent: -1, // Doesn't exist yet
332 IndeterminateComponent: 4,
333 LazyComponent: -1, // Doesn't exist yet
334 LegacyHiddenComponent: -1,
@@ -358,6 +362,7 @@ export function getInternalReactConstants(version: string): {
362 HostSingleton: -1, // Doesn't exist yet
363 HostText: 6,
364 IncompleteClassComponent: -1, // Doesn't exist yet
365 + IncompleteFunctionComponent: -1, // Doesn't exist yet
366 IndeterminateComponent: 0,
367 LazyComponent: -1, // Doesn't exist yet
368 LegacyHiddenComponent: -1,
@@ -391,6 +396,7 @@ export function getInternalReactConstants(version: string): {
396 CacheComponent,
397 ClassComponent,
398 IncompleteClassComponent,
399 + IncompleteFunctionComponent,
400 FunctionComponent,
401 IndeterminateComponent,
402 ForwardRef,
@@ -459,6 +465,7 @@ export function getInternalReactConstants(version: string): {
465 return 'Cache';
466 case ClassComponent:
467 case IncompleteClassComponent:
468 + case IncompleteFunctionComponent:
469 case FunctionComponent:
470 case IndeterminateComponent:
471 return getDisplayName(resolvedType);
@@ -624,6 +631,7 @@ export function attach(
631 HostComponent,
632 HostText,
633 IncompleteClassComponent,
634 + IncompleteFunctionComponent,
635 IndeterminateComponent,
636 LegacyHiddenComponent,
637 MemoComponent,
@@ -1061,6 +1069,7 @@ export function attach(
1069 case ClassComponent:
1070 case IncompleteClassComponent:
1071 return ElementTypeClass;
1072 + case IncompleteFunctionComponent:
1073 case FunctionComponent:
1074 case IndeterminateComponent:
1075 return ElementTypeFunction;
@@ -3059,6 +3068,7 @@ export function attach(
3068 switch (tag) {
3069 case ClassComponent:
3070 case IncompleteClassComponent:
3071 + case IncompleteFunctionComponent:
3072 case IndeterminateComponent:
3073 case FunctionComponent:
3074 global.$type = type;
@@ -3193,6 +3203,7 @@ export function attach(
3203 tag === ClassComponent ||
3204 tag === FunctionComponent ||
3205 tag === IncompleteClassComponent ||
3206 + tag === IncompleteFunctionComponent ||
3207 tag === IndeterminateComponent ||
3208 tag === MemoComponent ||
3209 tag === ForwardRef ||
@@ -3540,6 +3551,7 @@ export function attach(
3551 case IndeterminateComponent:
3552 global.$r = stateNode;
3553 break;
3554 + case IncompleteFunctionComponent:
3555 case FunctionComponent:
3556 global.$r = {
3557 hooks,
packages/react-devtools-shared/src/backend/types.js
+1
@@ -58,6 +58,7 @@ export type WorkTagMap = {
58 HostSingleton: WorkTag,
59 HostText: WorkTag,
60 IncompleteClassComponent: WorkTag,
61 + IncompleteFunctionComponent: WorkTag,
62 IndeterminateComponent: WorkTag,
63 LazyComponent: WorkTag,
64 LegacyHiddenComponent: WorkTag,
packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
+6 -13
@@ -223,23 +223,16 @@ describe('ReactCompositeComponent', () => {
223 const el = document.createElement('div');
224 const root = ReactDOMClient.createRoot(el);
225 await expect(async () => {
226 - await expect(async () => {
227 - await act(() => {
228 - root.render(<Child test="test" />);
229 - });
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.",
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
233 expect(el.textContent).toBe('');
234 });
235 +
236 it('should use default values for undefined props', async () => {
237 class Component extends React.Component {
238 static defaultProps = {prop: 'testKey'};
packages/react-reconciler/src/ReactFiber.js
+5 -16
@@ -41,7 +41,6 @@ import {
41 import {NoFlags, Placement, StaticMask} from './ReactFiberFlags';
42 import {ConcurrentRoot} from './ReactRootTags';
43 import {
44 - IndeterminateComponent,
44 ClassComponent,
45 HostRoot,
46 HostComponent,
@@ -245,19 +244,10 @@ export function isSimpleFunctionComponent(type: any): boolean {
244 );
245 }
246
248 -export function resolveLazyComponentTag(Component: Function): WorkTag {
249 - if (typeof Component === 'function') {
250 - return shouldConstruct(Component) ? ClassComponent : FunctionComponent;
251 - } else if (Component !== undefined && Component !== null) {
252 - const $$typeof = Component.$$typeof;
253 - if ($$typeof === REACT_FORWARD_REF_TYPE) {
254 - return ForwardRef;
255 - }
256 - if ($$typeof === REACT_MEMO_TYPE) {
257 - return MemoComponent;
258 - }
259 - }
260 - return IndeterminateComponent;
247 +export function isFunctionClassComponent(
248 + type: (...args: Array<any>) => mixed,
249 +): boolean {
250 + return shouldConstruct(type);
251 }
252
253 // This is used to create an alternate fiber to do work on.
@@ -348,7 +338,6 @@ export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
338 workInProgress._debugInfo = current._debugInfo;
339 workInProgress._debugNeedsRemount = current._debugNeedsRemount;
340 switch (workInProgress.tag) {
351 - case IndeterminateComponent:
341 case FunctionComponent:
342 case SimpleMemoComponent:
343 workInProgress.type = resolveFunctionForHotReloading(current.type);
@@ -489,7 +478,7 @@ export function createFiberFromTypeAndProps(
478 mode: TypeOfMode,
479 lanes: Lanes,
480 ): Fiber {
492 - let fiberTag = IndeterminateComponent;
481 + let fiberTag = FunctionComponent;
482 // The resolved type is set if we know what the final type will be. I.e. it's not lazy.
483 let resolvedType = type;
484 if (typeof type === 'function') {
packages/react-reconciler/src/ReactFiberBeginWork.js
+100 -160
@@ -46,7 +46,6 @@ import {
46 setIsStrictModeForDevtools,
47 } from './ReactFiberDevToolsHook';
48 import {
49 - IndeterminateComponent,
49 FunctionComponent,
50 ClassComponent,
51 HostRoot,
@@ -67,6 +66,7 @@ import {
66 SimpleMemoComponent,
67 LazyComponent,
68 IncompleteClassComponent,
69 + IncompleteFunctionComponent,
70 ScopeComponent,
71 OffscreenComponent,
72 LegacyHiddenComponent,
@@ -114,7 +114,12 @@ import shallowEqual from 'shared/shallowEqual';
114 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
115 import getComponentNameFromType from 'shared/getComponentNameFromType';
116 import ReactStrictModeWarnings from './ReactStrictModeWarnings';
117 -import {REACT_LAZY_TYPE, getIteratorFn} from 'shared/ReactSymbols';
117 +import {
118 + REACT_LAZY_TYPE,
119 + REACT_FORWARD_REF_TYPE,
120 + REACT_MEMO_TYPE,
121 + getIteratorFn,
122 +} from 'shared/ReactSymbols';
123 import {
124 getCurrentFiberOwnerNameInDevOrNull,
125 setIsRendering,
@@ -242,12 +247,12 @@ import {
247 } from './ReactFiberClassComponent';
248 import {resolveDefaultProps} from './ReactFiberLazyComponent';
249 import {
245 - resolveLazyComponentTag,
250 createFiberFromTypeAndProps,
251 createFiberFromFragment,
252 createFiberFromOffscreen,
253 createWorkInProgress,
254 isSimpleFunctionComponent,
255 + isFunctionClassComponent,
256 } from './ReactFiber';
257 import {
258 retryDehydratedSuspenseBoundary,
@@ -303,7 +308,6 @@ export const SelectiveHydrationException: mixed = new Error(
308 let didReceiveUpdate: boolean = false;
309
310 let didWarnAboutBadClass;
306 -let didWarnAboutModulePatternComponent;
311 let didWarnAboutContextTypeOnFunctionComponent;
312 let didWarnAboutGetDerivedStateOnFunctionComponent;
313 let didWarnAboutFunctionRefs;
@@ -314,7 +318,6 @@ let didWarnAboutDefaultPropsOnFunctionComponent;
318
319 if (__DEV__) {
320 didWarnAboutBadClass = ({}: {[string]: boolean});
317 - didWarnAboutModulePatternComponent = ({}: {[string]: boolean});
321 didWarnAboutContextTypeOnFunctionComponent = ({}: {[string]: boolean});
322 didWarnAboutGetDerivedStateOnFunctionComponent = ({}: {[string]: boolean});
323 didWarnAboutFunctionRefs = ({}: {[string]: boolean});
@@ -1044,6 +1047,26 @@ function markRef(current: Fiber | null, workInProgress: Fiber) {
1047 }
1048 }
1049
1050 +function mountIncompleteFunctionComponent(
1051 + _current: null | Fiber,
1052 + workInProgress: Fiber,
1053 + Component: any,
1054 + nextProps: any,
1055 + renderLanes: Lanes,
1056 +) {
1057 + resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress);
1058 +
1059 + workInProgress.tag = FunctionComponent;
1060 +
1061 + return updateFunctionComponent(
1062 + null,
1063 + workInProgress,
1064 + Component,
1065 + nextProps,
1066 + renderLanes,
1067 + );
1068 +}
1069 +
1070 function updateFunctionComponent(
1071 current: null | Fiber,
1072 workInProgress: Fiber,
@@ -1051,6 +1074,43 @@ function updateFunctionComponent(
1074 nextProps: any,
1075 renderLanes: Lanes,
1076 ) {
1077 + if (__DEV__) {
1078 + if (
1079 + Component.prototype &&
1080 + typeof Component.prototype.render === 'function'
1081 + ) {
1082 + const componentName = getComponentNameFromType(Component) || 'Unknown';
1083 +
1084 + if (!didWarnAboutBadClass[componentName]) {
1085 + console.error(
1086 + "The <%s /> component appears to have a render method, but doesn't extend React.Component. " +
1087 + 'This is likely to cause errors. Change %s to extend React.Component instead.',
1088 + componentName,
1089 + componentName,
1090 + );
1091 + didWarnAboutBadClass[componentName] = true;
1092 + }
1093 + }
1094 +
1095 + if (workInProgress.mode & StrictLegacyMode) {
1096 + ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null);
1097 + }
1098 +
1099 + if (current === null) {
1100 + // Some validations were previously done in mountIndeterminateComponent however and are now run
1101 + // in updateFuntionComponent but only on mount
1102 + validateFunctionComponentInDev(workInProgress, workInProgress.type);
1103 +
1104 + if (disableLegacyContext && Component.contextTypes) {
1105 + console.error(
1106 + '%s uses the legacy contextTypes API which was removed in React 19. ' +
1107 + 'Use React.createContext() with React.useContext() instead.',
1108 + getComponentNameFromType(Component) || 'Unknown',
1109 + );
1110 + }
1111 + }
1112 + }
1113 +
1114 let context;
1115 if (!disableLegacyContext) {
1116 const unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
@@ -1697,64 +1757,64 @@ function mountLazyComponent(
1757 let Component = init(payload);
1758 // Store the unwrapped component in the type.
1759 workInProgress.type = Component;
1700 - const resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component));
1760 +
1761 const resolvedProps = resolveDefaultProps(Component, props);
1702 - let child;
1703 - switch (resolvedTag) {
1704 - case FunctionComponent: {
1762 + if (typeof Component === 'function') {
1763 + if (isFunctionClassComponent(Component)) {
1764 + workInProgress.tag = ClassComponent;
1765 if (__DEV__) {
1706 - validateFunctionComponentInDev(workInProgress, Component);
1766 workInProgress.type = Component =
1708 - resolveFunctionForHotReloading(Component);
1767 + resolveClassForHotReloading(Component);
1768 }
1710 - child = updateFunctionComponent(
1769 + return updateClassComponent(
1770 null,
1771 workInProgress,
1772 Component,
1773 resolvedProps,
1774 renderLanes,
1775 );
1717 - return child;
1718 - }
1719 - case ClassComponent: {
1776 + } else {
1777 + workInProgress.tag = FunctionComponent;
1778 if (__DEV__) {
1779 + validateFunctionComponentInDev(workInProgress, Component);
1780 workInProgress.type = Component =
1722 - resolveClassForHotReloading(Component);
1781 + resolveFunctionForHotReloading(Component);
1782 }
1724 - child = updateClassComponent(
1783 + return updateFunctionComponent(
1784 null,
1785 workInProgress,
1786 Component,
1787 resolvedProps,
1788 renderLanes,
1789 );
1731 - return child;
1790 }
1733 - case ForwardRef: {
1791 + } else if (Component !== undefined && Component !== null) {
1792 + const $$typeof = Component.$$typeof;
1793 + if ($$typeof === REACT_FORWARD_REF_TYPE) {
1794 + workInProgress.tag = ForwardRef;
1795 if (__DEV__) {
1796 workInProgress.type = Component =
1797 resolveForwardRefForHotReloading(Component);
1798 }
1738 - child = updateForwardRef(
1799 + return updateForwardRef(
1800 null,
1801 workInProgress,
1802 Component,
1803 resolvedProps,
1804 renderLanes,
1805 );
1745 - return child;
1746 - }
1747 - case MemoComponent: {
1748 - child = updateMemoComponent(
1806 + } else if ($$typeof === REACT_MEMO_TYPE) {
1807 + workInProgress.tag = MemoComponent;
1808 + return updateMemoComponent(
1809 null,
1810 workInProgress,
1811 Component,
1812 resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too
1813 renderLanes,
1814 );
1755 - return child;
1815 }
1816 }
1817 +
1818 let hint = '';
1819 if (__DEV__) {
1820 if (
@@ -1814,133 +1874,6 @@ function mountIncompleteClassComponent(
1874 );
1875 }
1876
1817 -function mountIndeterminateComponent(
1818 - _current: null | Fiber,
1819 - workInProgress: Fiber,
1820 - Component: $FlowFixMe,
1821 - renderLanes: Lanes,
1822 -) {
1823 - resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress);
1824 -
1825 - const props = workInProgress.pendingProps;
1826 - let context;
1827 - if (!disableLegacyContext) {
1828 - const unmaskedContext = getUnmaskedContext(
1829 - workInProgress,
1830 - Component,
1831 - false,
1832 - );
1833 - context = getMaskedContext(workInProgress, unmaskedContext);
1834 - }
1835 -
1836 - prepareToReadContext(workInProgress, renderLanes);
1837 - let value;
1838 - let hasId;
1839 -
1840 - if (enableSchedulingProfiler) {
1841 - markComponentRenderStarted(workInProgress);
1842 - }
1843 - if (__DEV__) {
1844 - if (
1845 - Component.prototype &&
1846 - typeof Component.prototype.render === 'function'
1847 - ) {
1848 - const componentName = getComponentNameFromType(Component) || 'Unknown';
1849 -
1850 - if (!didWarnAboutBadClass[componentName]) {
1851 - console.error(
1852 - "The <%s /> component appears to have a render method, but doesn't extend React.Component. " +
1853 - 'This is likely to cause errors. Change %s to extend React.Component instead.',
1854 - componentName,
1855 - componentName,
1856 - );
1857 - didWarnAboutBadClass[componentName] = true;
1858 - }
1859 - }
1860 -
1861 - if (workInProgress.mode & StrictLegacyMode) {
1862 - ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null);
1863 - }
1864 -
1865 - setIsRendering(true);
1866 - ReactCurrentOwner.current = workInProgress;
1867 - value = renderWithHooks(
1868 - null,
1869 - workInProgress,
1870 - Component,
1871 - props,
1872 - context,
1873 - renderLanes,
1874 - );
1875 - hasId = checkDidRenderIdHook();
1876 - setIsRendering(false);
1877 - } else {
1878 - value = renderWithHooks(
1879 - null,
1880 - workInProgress,
1881 - Component,
1882 - props,
1883 - context,
1884 - renderLanes,
1885 - );
1886 - hasId = checkDidRenderIdHook();
1887 - }
1888 - if (enableSchedulingProfiler) {
1889 - markComponentRenderStopped();
1890 - }
1891 -
1892 - // React DevTools reads this flag.
1893 - workInProgress.flags |= PerformedWork;
1894 -
1895 - if (__DEV__) {
1896 - // Support for module components is deprecated and is removed behind a flag.
1897 - // Whether or not it would crash later, we want to show a good message in DEV first.
1898 - if (
1899 - typeof value === 'object' &&
1900 - value !== null &&
1901 - typeof value.render === 'function' &&
1902 - value.$$typeof === undefined
1903 - ) {
1904 - const componentName = getComponentNameFromType(Component) || 'Unknown';
1905 - if (!didWarnAboutModulePatternComponent[componentName]) {
1906 - console.error(
1907 - 'The <%s /> component appears to be a function component that returns a class instance. ' +
1908 - 'Change %s to a class that extends React.Component instead. ' +
1909 - "If you can't use a class try assigning the prototype on the function as a workaround. " +
1910 - "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " +
1911 - 'cannot be called with `new` by React.',
1912 - componentName,
1913 - componentName,
1914 - componentName,
1915 - );
1916 - didWarnAboutModulePatternComponent[componentName] = true;
1917 - }
1918 - }
1919 - }
1920 -
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 -
1933 - if (getIsHydrating() && hasId) {
1934 - pushMaterializedTreeId(workInProgress);
1935 - }
1936 -
1937 - reconcileChildren(null, workInProgress, value, renderLanes);
1938 - if (__DEV__) {
1939 - validateFunctionComponentInDev(workInProgress, Component);
1940 - }
1941 - return workInProgress.child;
1942 -}
1943 -
1877 function validateFunctionComponentInDev(workInProgress: Fiber, Component: any) {
1878 if (__DEV__) {
1879 if (Component) {
@@ -3972,14 +3905,6 @@ function beginWork(
3905 workInProgress.lanes = NoLanes;
3906
3907 switch (workInProgress.tag) {
3975 - case IndeterminateComponent: {
3976 - return mountIndeterminateComponent(
3977 - current,
3978 - workInProgress,
3979 - workInProgress.type,
3980 - renderLanes,
3981 - );
3982 - }
3908 case LazyComponent: {
3909 const elementType = workInProgress.elementType;
3910 return mountLazyComponent(
@@ -4102,6 +4027,21 @@ function beginWork(
4027 renderLanes,
4028 );
4029 }
4030 + case IncompleteFunctionComponent: {
4031 + const Component = workInProgress.type;
4032 + const unresolvedProps = workInProgress.pendingProps;
4033 + const resolvedProps =
4034 + workInProgress.elementType === Component
4035 + ? unresolvedProps
4036 + : resolveDefaultProps(Component, unresolvedProps);
4037 + return mountIncompleteFunctionComponent(
4038 + current,
4039 + workInProgress,
4040 + Component,
4041 + resolvedProps,
4042 + renderLanes,
4043 + );
4044 + }
4045 case SuspenseListComponent: {
4046 return updateSuspenseListComponent(current, workInProgress, renderLanes);
4047 }
packages/react-reconciler/src/ReactFiberClassComponent.js
+7 -11
@@ -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) {
packages/react-reconciler/src/ReactFiberCompleteWork.js
+2 -2
@@ -45,7 +45,6 @@ import {
45 import {now} from './Scheduler';
46
47 import {
48 - IndeterminateComponent,
48 FunctionComponent,
49 ClassComponent,
50 HostRoot,
@@ -66,6 +65,7 @@ import {
65 SimpleMemoComponent,
66 LazyComponent,
67 IncompleteClassComponent,
68 + IncompleteFunctionComponent,
69 ScopeComponent,
70 OffscreenComponent,
71 LegacyHiddenComponent,
@@ -949,10 +949,10 @@ function completeWork(
949 // for hydration.
950 popTreeContext(workInProgress);
951 switch (workInProgress.tag) {
952 - case IndeterminateComponent:
952 case LazyComponent:
953 case SimpleMemoComponent:
954 case FunctionComponent:
955 + case IncompleteFunctionComponent:
956 case ForwardRef:
957 case Fragment:
958 case Mode:
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/ReactFiberThrow.js
+8
@@ -20,6 +20,7 @@ import {
20 ClassComponent,
21 HostRoot,
22 IncompleteClassComponent,
23 + IncompleteFunctionComponent,
24 FunctionComponent,
25 ForwardRef,
26 SimpleMemoComponent,
@@ -262,6 +263,13 @@ function markSuspenseBoundaryShouldCapture(
263 update.tag = ForceUpdate;
264 enqueueUpdate(sourceFiber, update, SyncLane);
265 }
266 + } else if (sourceFiber.tag === FunctionComponent) {
267 + const currentSourceFiber = sourceFiber.alternate;
268 + if (currentSourceFiber === null) {
269 + // This is a new mount. Change the tag so it's not mistaken for a
270 + // completed function component.
271 + sourceFiber.tag = IncompleteFunctionComponent;
272 + }
273 }
274
275 // The source fiber did not complete. Mark it with Sync priority to
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`.
@@ -3807,7 +3800,6 @@ export function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber: Fiber) {
3800
3801 const tag = fiber.tag;
3802 if (
3810 - tag !== IndeterminateComponent &&
3803 tag !== HostRoot &&
3804 tag !== ClassComponent &&
3805 tag !== FunctionComponent &&
packages/react-reconciler/src/ReactWorkTags.js
+3 -2
@@ -35,11 +35,11 @@ export type WorkTag =
35 | 24
36 | 25
37 | 26
38 - | 27;
38 + | 27
39 + | 28;
40
41 export const FunctionComponent = 0;
42 export const ClassComponent = 1;
42 -export const IndeterminateComponent = 2; // Before we know whether it is function or class
43 export const HostRoot = 3; // Root of a host tree. Could be nested inside another node.
44 export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
45 export const HostComponent = 5;
@@ -64,3 +64,4 @@ export const CacheComponent = 24;
64 export const TracingMarkerComponent = 25;
65 export const HostHoistable = 26;
66 export const HostSingleton = 27;
67 +export const IncompleteFunctionComponent = 28;
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-server/src/ReactFizzServer.js
+2 -32
@@ -1410,7 +1410,6 @@ function renderClassComponent(
1410 }
1411
1412 const didWarnAboutBadClass: {[string]: boolean} = {};
1413 -const didWarnAboutModulePatternComponent: {[string]: boolean} = {};
1413 const didWarnAboutContextTypeOnFunctionComponent: {[string]: boolean} = {};
1414 const didWarnAboutGetDerivedStateOnFunctionComponent: {[string]: boolean} = {};
1415 let didWarnAboutReassigningProps = false;
@@ -1418,9 +1417,7 @@ const didWarnAboutDefaultPropsOnFunctionComponent: {[string]: boolean} = {};
1417 let didWarnAboutGenerators = false;
1418 let didWarnAboutMaps = false;
1419
1421 -// This would typically be a function component but we still support module pattern
1422 -// components for some reason.
1423 -function renderIndeterminateComponent(
1420 +function renderFunctionComponent(
1421 request: Request,
1422 task: Task,
1423 keyPath: KeyNode,
@@ -1465,33 +1462,6 @@ function renderIndeterminateComponent(
1462 const actionStateCount = getActionStateCount();
1463 const actionStateMatchingIndex = getActionStateMatchingIndex();
1464
1468 - if (__DEV__) {
1469 - // Support for module components is deprecated and is removed behind a flag.
1470 - // Whether or not it would crash later, we want to show a good message in DEV first.
1471 - if (
1472 - typeof value === 'object' &&
1473 - value !== null &&
1474 - typeof value.render === 'function' &&
1475 - value.$$typeof === undefined
1476 - ) {
1477 - const componentName = getComponentNameFromType(Component) || 'Unknown';
1478 - if (!didWarnAboutModulePatternComponent[componentName]) {
1479 - console.error(
1480 - 'The <%s /> component appears to be a function component that returns a class instance. ' +
1481 - 'Change %s to a class that extends React.Component instead. ' +
1482 - "If you can't use a class try assigning the prototype on the function as a workaround. " +
1483 - "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " +
1484 - 'cannot be called with `new` by React.',
1485 - componentName,
1486 - componentName,
1487 - componentName,
1488 - );
1489 - didWarnAboutModulePatternComponent[componentName] = true;
1490 - }
1491 - }
1492 - }
1493 -
1494 - // Proceed under the assumption that this is a function component
1465 if (__DEV__) {
1466 if (disableLegacyContext && Component.contextTypes) {
1467 console.error(
@@ -1817,7 +1787,7 @@ function renderElement(
1787 renderClassComponent(request, task, keyPath, type, props);
1788 return;
1789 } else {
1820 - renderIndeterminateComponent(request, task, keyPath, type, props);
1790 + renderFunctionComponent(request, task, keyPath, type, props);
1791 return;
1792 }
1793 }