RN: Migrate View to Component Syntax (#52018)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52018

Migrates `View` to use Flow's [Component Syntax](https://flow.org/en/docs/react/component-syntax/).

Component Syntax offers more concise syntax for expressing component definitions and eliminates the need for `View` to be cast as a component type. Component types improve type safety by more accurately modeling `ref` as props and enabling the use of modern type utilities such as `React.PropsOf<Component>` and `React.RefOf<Component>`.

This represents the first introduction of Component Syntax in the open source React Native repository, which is lowered to standard JavaScript through the use of [`hermes-parser`](https://www.npmjs.com/package/hermes-parser) and the following supporting packages:

- [`babel-plugin-syntax-hermes-parser`](https://www.npmjs.com/package/babel-plugin-syntax-hermes-parser)
- [`hermes-eslint`](https://www.npmjs.com/package/hermes-eslint)
- [`prettier-plugin-hermes-parser`](https://www.npmjs.com/package/prettier-plugin-hermes-parser)

By default, [`react-native/babel-preset`](https://www.npmjs.com/package/react-native/babel-preset) already provides configuration for `babel-plugin-syntax-hermes-parser`.

Although this is the first usage of Component Syntax, there are already Flow syntax features used in React Native that have required `hermes-parser` (e.g. [type casting expressions](https://flow.org/en/docs/types/casting/), [type guards](https://flow.org/en/docs/types/type-guards/), [const expressions](https://flow.org/en/docs/types/const-expression/)). As such, we do not anticipate this to introduce new build configuration problems for developers.

Changelog:
[Internal]

Reviewed By: javache, NickGerleman

Differential Revision: D76632879

fbshipit-source-id: e0a83dfc2e2014933000083de1825a311faad707
This commit is contained in:
Tim Yung
2025-06-16 06:52:07 -07:00
committed by Facebook GitHub Bot
parent ffe928ce50
commit f53f6137e6
3 changed files with 14 additions and 14 deletions
+1
View File
@@ -48,6 +48,7 @@ packages/react-native/flow/
[options]
enums=true
casting_syntax=both
component_syntax=true
emoji=true
+12 -13
View File
@@ -16,11 +16,6 @@ import ViewNativeComponent from './ViewNativeComponent';
import * as React from 'react';
import {use} from 'react';
type PropsWithRef = $ReadOnly<{
ref?: React.RefSetter<React.ElementRef<typeof ViewNativeComponent>>,
...ViewProps,
}>;
/**
* The most fundamental component for building a UI, View is a container that
* supports layout with flexbox, style, some touch handling, and accessibility
@@ -28,7 +23,10 @@ type PropsWithRef = $ReadOnly<{
*
* @see https://reactnative.dev/docs/view
*/
function View(props: PropsWithRef): React.Node {
export default component View(
ref?: React.RefSetter<React.ElementRef<typeof ViewNativeComponent>>,
...props: ViewProps
) {
const hasTextAncestor = use(TextAncestor);
let actualView;
@@ -55,7 +53,7 @@ function View(props: PropsWithRef): React.Node {
} = props;
// Since we destructured props, we can now treat it as mutable
const processedProps = otherProps as {...PropsWithRef};
const processedProps = otherProps as {...ViewProps};
const parsedAriaLabelledBy = ariaLabelledBy?.split(/\s*,\s*/g);
if (parsedAriaLabelledBy !== undefined) {
@@ -118,7 +116,12 @@ function View(props: PropsWithRef): React.Node {
};
}
actualView = <ViewNativeComponent {...processedProps} />;
actualView =
ref == null ? (
<ViewNativeComponent {...processedProps} />
) : (
<ViewNativeComponent {...processedProps} ref={ref} />
);
} else {
const {
accessibilityElementsHidden,
@@ -198,6 +201,7 @@ function View(props: PropsWithRef): React.Node {
: importantForAccessibility
}
nativeID={id ?? nativeID}
ref={ref}
/>
);
}
@@ -207,8 +211,3 @@ function View(props: PropsWithRef): React.Node {
}
return actualView;
}
export default View as component(
ref?: React.RefSetter<React.ElementRef<typeof ViewNativeComponent>>,
...props: ViewProps
);
@@ -3515,7 +3515,7 @@ declare export default typeof ReactNativeViewAttributes;
`;
exports[`public API should not change unintentionally Libraries/Components/View/View.js 1`] = `
"declare export default component(
"declare export default component View(
ref?: React.RefSetter<React.ElementRef<typeof ViewNativeComponent>>,
...props: ViewProps
);