From 45804af18d589fd2c181f3b020f07661c46b73ea Mon Sep 17 00:00:00 2001 From: Sam Zhou Date: Mon, 21 Oct 2024 16:17:41 -0700 Subject: [PATCH] [flow] Eliminate usage of more than 1-arg `React.AbstractComponent` in React codebase (#31314) ## Summary In order to adopt react 19's ref-as-prop model, Flow needs to eliminate all the places where they are treated differently. `React.AbstractComponent` is the worst example of this, and we need to eliminate it. This PR eliminates them from the react repo, and only keeps the one that has 1 argument of props. ## How did you test this change? yarn flow --- .eslintrc.js | 1 + packages/react-devtools-inline/src/frontend.js | 2 +- .../src/devtools/ContextMenu/types.js | 8 +++++--- .../src/devtools/views/Components/Components.js | 2 +- .../src/devtools/views/portaledContent.js | 4 ++-- .../src/app/InspectableElements/CustomHooks.js | 6 ++++-- packages/react-markup/src/ReactMarkupServer.js | 2 +- .../src/ReactNativeFiberHostComponent.js | 5 ++--- .../src/ReactNativePublicCompat.js | 4 ++-- .../src/ReactNativeTypes.js | 17 ++++++++--------- .../react-reconciler/src/ReactTestSelectors.js | 4 ++-- packages/react-server/src/ReactFlightServer.js | 2 +- packages/react/index.development.js | 6 +----- packages/react/index.js | 6 +----- packages/react/src/ReactForwardRef.js | 5 ++++- 15 files changed, 36 insertions(+), 38 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 3366a38517c93..faeb5077f21a8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -569,6 +569,7 @@ module.exports = { React$Node: 'readonly', React$Portal: 'readonly', React$Ref: 'readonly', + React$RefSetter: 'readonly', ReadableStreamController: 'readonly', ReadableStreamReader: 'readonly', RequestInfo: 'readonly', diff --git a/packages/react-devtools-inline/src/frontend.js b/packages/react-devtools-inline/src/frontend.js index f96d29ac50745..056d5d2c27052 100644 --- a/packages/react-devtools-inline/src/frontend.js +++ b/packages/react-devtools-inline/src/frontend.js @@ -52,7 +52,7 @@ export function initialize( bridge?: FrontendBridge, store?: Store, } = {}, -): React.AbstractComponent { +): React.ComponentType { if (bridge == null) { bridge = createBridge(contentWindow); } diff --git a/packages/react-devtools-shared/src/devtools/ContextMenu/types.js b/packages/react-devtools-shared/src/devtools/ContextMenu/types.js index 2436fcc2d1b80..e2e8cecae33ac 100644 --- a/packages/react-devtools-shared/src/devtools/ContextMenu/types.js +++ b/packages/react-devtools-shared/src/devtools/ContextMenu/types.js @@ -7,7 +7,7 @@ * @flow */ -import type {Node as ReactNode, AbstractComponent, ElementRef} from 'react'; +import type {Node as ReactNode} from 'react'; export type ContextMenuItem = { onClick: () => void, @@ -25,5 +25,7 @@ export type ContextMenuHandle = { hide(): void, }; -export type ContextMenuComponent = AbstractComponent<{}, ContextMenuHandle>; -export type ContextMenuRef = {current: ElementRef | null}; +/*:: +export type ContextMenuComponent = component(ref: React$RefSetter); +*/ +export type ContextMenuRef = {current: ContextMenuHandle | null}; diff --git a/packages/react-devtools-shared/src/devtools/views/Components/Components.js b/packages/react-devtools-shared/src/devtools/views/Components/Components.js index f0b309d3f1a2a..995f1d92cf323 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/Components.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/Components.js @@ -246,4 +246,4 @@ function setResizeCSSVariable( } } -export default (portaledContent(Components): React$AbstractComponent<{}>); +export default (portaledContent(Components): React$ComponentType<{}>); diff --git a/packages/react-devtools-shared/src/devtools/views/portaledContent.js b/packages/react-devtools-shared/src/devtools/views/portaledContent.js index efd0a4e30d272..b2be6867d93b8 100644 --- a/packages/react-devtools-shared/src/devtools/views/portaledContent.js +++ b/packages/react-devtools-shared/src/devtools/views/portaledContent.js @@ -17,8 +17,8 @@ import ThemeProvider from './ThemeProvider'; export type Props = {portalContainer?: Element, ...}; export default function portaledContent( - Component: React$AbstractComponent, -): React$AbstractComponent { + Component: React$ComponentType, +): React$ComponentType { return function PortaledContent({portalContainer, ...rest}: Props) { const store = useContext(StoreContext); diff --git a/packages/react-devtools-shell/src/app/InspectableElements/CustomHooks.js b/packages/react-devtools-shell/src/app/InspectableElements/CustomHooks.js index a05acf138d3d3..487c0be4da750 100644 --- a/packages/react-devtools-shell/src/app/InspectableElements/CustomHooks.js +++ b/packages/react-devtools-shell/src/app/InspectableElements/CustomHooks.js @@ -72,7 +72,7 @@ function useDeepHookF() { const ContextA = createContext('A'); const ContextB = createContext('B'); -function FunctionWithHooks(props: any, ref: React$Ref) { +function FunctionWithHooks(props: any, ref: React$RefSetter) { const [count, updateCount] = useState(0); // eslint-disable-next-line no-unused-vars const contextValueA = useContext(ContextA); @@ -108,7 +108,9 @@ function FunctionWithHooks(props: any, ref: React$Ref) { const MemoWithHooks = memo(FunctionWithHooks); const ForwardRefWithHooks = forwardRef(FunctionWithHooks); -function wrapWithHoc(Component: (props: any, ref: React$Ref) => any) { +function wrapWithHoc( + Component: (props: any, ref: React$RefSetter) => any, +) { function Hoc() { return ; } diff --git a/packages/react-markup/src/ReactMarkupServer.js b/packages/react-markup/src/ReactMarkupServer.js index d0cd8b96f823b..d3950c568f7ce 100644 --- a/packages/react-markup/src/ReactMarkupServer.js +++ b/packages/react-markup/src/ReactMarkupServer.js @@ -46,7 +46,7 @@ import { type ReactMarkupNodeList = // This is the intersection of ReactNodeList and ReactClientValue minus // Client/ServerReferences. - | React$Element> + | React$Element> | LazyComponent | React$Element | string diff --git a/packages/react-native-renderer/src/ReactNativeFiberHostComponent.js b/packages/react-native-renderer/src/ReactNativeFiberHostComponent.js index 80151468dd24a..28d259fc5d0e5 100644 --- a/packages/react-native-renderer/src/ReactNativeFiberHostComponent.js +++ b/packages/react-native-renderer/src/ReactNativeFiberHostComponent.js @@ -7,9 +7,8 @@ * @flow */ -import type {ElementRef} from 'react'; import type { - HostComponent, + HostInstance, MeasureInWindowOnSuccessCallback, MeasureLayoutOnSuccessCallback, MeasureOnSuccessCallback, @@ -72,7 +71,7 @@ class ReactNativeFiberHostComponent implements INativeMethods { } measureLayout( - relativeToNativeNode: number | ElementRef>, + relativeToNativeNode: number | HostInstance, onSuccess: MeasureLayoutOnSuccessCallback, onFail?: () => void /* currently unused */, ) { diff --git a/packages/react-native-renderer/src/ReactNativePublicCompat.js b/packages/react-native-renderer/src/ReactNativePublicCompat.js index c50b881c150c1..c8883261f7fc4 100644 --- a/packages/react-native-renderer/src/ReactNativePublicCompat.js +++ b/packages/react-native-renderer/src/ReactNativePublicCompat.js @@ -32,7 +32,7 @@ import { export function findHostInstance_DEPRECATED( componentOrHandle: ?(ElementRef | number), -): ?ElementRef> { +): ?ElementRef> { if (__DEV__) { const owner = currentOwner; if (owner !== null && isRendering && owner.stateNode !== null) { @@ -225,7 +225,7 @@ export function getNodeFromInternalInstanceHandle( // Should have been PublicInstance from ReactFiberConfigFabric type FabricPublicInstance = mixed; // Should have been PublicInstance from ReactFiberConfigNative -type PaperPublicInstance = HostComponent; +type PaperPublicInstance = HostComponent; // Remove this once Paper is no longer supported and DOM Node API are enabled by default in RN. export function isChildPublicInstance( diff --git a/packages/react-native-renderer/src/ReactNativeTypes.js b/packages/react-native-renderer/src/ReactNativeTypes.js index 540ac1cf2aa70..80d9df6f84c5c 100644 --- a/packages/react-native-renderer/src/ReactNativeTypes.js +++ b/packages/react-native-renderer/src/ReactNativeTypes.js @@ -9,12 +9,7 @@ * @flow strict */ -import type { - ElementRef, - ElementType, - MixedElement, - AbstractComponent, -} from 'react'; +import type {ElementRef, ElementType, MixedElement} from 'react'; export type MeasureOnSuccessCallback = ( x: number, @@ -137,7 +132,9 @@ declare const ensureNativeMethodsAreSynced: NativeMethods; (ensureNativeMethodsAreSynced: INativeMethods); export type HostInstance = NativeMethods; -export type HostComponent = AbstractComponent; +/*:: +export type HostComponent = component(ref: React$RefSetter, ...Config); +*/ type InspectorDataProps = $ReadOnly<{ [propName: string]: string, @@ -208,8 +205,10 @@ export type ReactNativeType = { componentOrHandle: ?(ElementRef | number), ): ?number, isChildPublicInstance( - parent: PublicInstance | HostComponent, - child: PublicInstance | HostComponent, + // eslint-disable-next-line no-undef + parent: PublicInstance | HostComponent, + // eslint-disable-next-line no-undef + child: PublicInstance | HostComponent, ): boolean, dispatchCommand( handle: HostInstance, diff --git a/packages/react-reconciler/src/ReactTestSelectors.js b/packages/react-reconciler/src/ReactTestSelectors.js index 34f0529f8cdeb..de3e95c281e60 100644 --- a/packages/react-reconciler/src/ReactTestSelectors.js +++ b/packages/react-reconciler/src/ReactTestSelectors.js @@ -48,7 +48,7 @@ type Type = symbol | number; type ComponentSelector = { $$typeof: Type, - value: React$AbstractComponent, + value: React$ComponentType, }; type HasPseudoClassSelector = { @@ -79,7 +79,7 @@ type Selector = | TestNameSelector; export function createComponentSelector( - component: React$AbstractComponent, + component: React$ComponentType, ): ComponentSelector { return { $$typeof: COMPONENT_TYPE, diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index ca004a6e7971f..ca238271e20d6 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -297,7 +297,7 @@ type ReactJSONValue = // Serializable values export type ReactClientValue = // Server Elements and Lazy Components are unwrapped on the Server - | React$Element> + | React$Element> | LazyComponent // References are passed by their value | ClientReference diff --git a/packages/react/index.development.js b/packages/react/index.development.js index bf08db7a58418..398e67240416d 100644 --- a/packages/react/index.development.js +++ b/packages/react/index.development.js @@ -9,14 +9,10 @@ // Keep in sync with https://github.com/facebook/flow/blob/main/lib/react.js export type ComponentType<-P> = React$ComponentType

; -export type AbstractComponent< - -Config, - +Instance = mixed, -> = React$AbstractComponent; +export type AbstractComponent<-Config> = React$AbstractComponent; export type ElementType = React$ElementType; export type Element<+C> = React$Element; export type Key = React$Key; -export type Ref = React$Ref; export type Node = React$Node; export type Context = React$Context; export type Portal = React$Portal; diff --git a/packages/react/index.js b/packages/react/index.js index 0c048c4e9c297..2edb0a2c1de09 100644 --- a/packages/react/index.js +++ b/packages/react/index.js @@ -9,15 +9,11 @@ // Keep in sync with https://github.com/facebook/flow/blob/main/lib/react.js export type ComponentType<-P> = React$ComponentType

; -export type AbstractComponent< - -Config, - +Instance = mixed, -> = React$AbstractComponent; +export type AbstractComponent<-Config> = React$AbstractComponent; export type ElementType = React$ElementType; export type Element<+C> = React$Element; export type MixedElement = React$Element; export type Key = React$Key; -export type Ref = React$Ref; export type Node = React$Node; export type Context = React$Context; export type Portal = React$Portal; diff --git a/packages/react/src/ReactForwardRef.js b/packages/react/src/ReactForwardRef.js index 8978763ba15ef..4f891d609ff48 100644 --- a/packages/react/src/ReactForwardRef.js +++ b/packages/react/src/ReactForwardRef.js @@ -10,7 +10,10 @@ import {REACT_FORWARD_REF_TYPE, REACT_MEMO_TYPE} from 'shared/ReactSymbols'; export function forwardRef( - render: (props: Props, ref: React$Ref) => React$Node, + render: ( + props: Props, + ref: React$RefSetter>, + ) => React$Node, ) { if (__DEV__) { if (render != null && render.$$typeof === REACT_MEMO_TYPE) {