Skip to content

Commit eedd60b

Browse files
EvanBaconfacebook-github-bot
authored andcommitted
chore: convert View to React 19 (facebook#51023)
Summary: - Convert View implementation to React 19: - Remove legacy `forwardRef` in favor of built-in `ref` prop. - Use `use` API instead of `useContext`. - Drop the extraneous `.Provider` for `TextAncestor` context. - Remove `displayName` in favor of component name. I'm not 100% sure this is a full fallback but it is valid according to `react/display-name` eslint rule—https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/display-name.md - Based on discussion with Nicola Carti and Riccardo Cipolleschi. - I tried using flow `component` keyword but it's not enabled in this project. Given the `react-native` package is shipped untranspiled, it's probably safer to avoid newer flow types. - Overall matched the component style of LogBox. - It's unclear the exact right way to type a ref since it should be optional for external users of the component but required inside the component. Erring on the side of caution and using optional types so users don't get type errors when `ref` isn't defined. ## Changelog: [GENERAL] [BREAKING] Upgrade `View` component to React 19. <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests Pull Request resolved: facebook#51023 Test Plan: - Type checks should pass. Reviewed By: rshest Differential Revision: D74546184 Pulled By: yungsters fbshipit-source-id: b8257e3a75477c1117b19cd3f8e0843947b092ca
1 parent 5d7f35c commit eedd60b

File tree

3 files changed

+88
-100
lines changed

3 files changed

+88
-100
lines changed

packages/react-native/Libraries/Components/View/View.js

Lines changed: 86 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -23,108 +23,97 @@ export type Props = ViewProps;
2323
*
2424
* @see https://reactnative.dev/docs/view
2525
*/
26-
const View: component(
26+
function View({
27+
accessibilityElementsHidden,
28+
accessibilityLabel,
29+
accessibilityLabelledBy,
30+
accessibilityLiveRegion,
31+
accessibilityState,
32+
accessibilityValue,
33+
'aria-busy': ariaBusy,
34+
'aria-checked': ariaChecked,
35+
'aria-disabled': ariaDisabled,
36+
'aria-expanded': ariaExpanded,
37+
'aria-hidden': ariaHidden,
38+
'aria-label': ariaLabel,
39+
'aria-labelledby': ariaLabelledBy,
40+
'aria-live': ariaLive,
41+
'aria-selected': ariaSelected,
42+
'aria-valuemax': ariaValueMax,
43+
'aria-valuemin': ariaValueMin,
44+
'aria-valuenow': ariaValueNow,
45+
'aria-valuetext': ariaValueText,
46+
focusable,
47+
id,
48+
importantForAccessibility,
49+
nativeID,
50+
tabIndex,
51+
...otherProps
52+
}: $ReadOnly<{
2753
ref?: React.RefSetter<React.ElementRef<typeof ViewNativeComponent>>,
28-
...props: ViewProps
29-
) = React.forwardRef(
30-
(
31-
{
32-
accessibilityElementsHidden,
33-
accessibilityLabel,
34-
accessibilityLabelledBy,
35-
accessibilityLiveRegion,
36-
accessibilityState,
37-
accessibilityValue,
38-
'aria-busy': ariaBusy,
39-
'aria-checked': ariaChecked,
40-
'aria-disabled': ariaDisabled,
41-
'aria-expanded': ariaExpanded,
42-
'aria-hidden': ariaHidden,
43-
'aria-label': ariaLabel,
44-
'aria-labelledby': ariaLabelledBy,
45-
'aria-live': ariaLive,
46-
'aria-selected': ariaSelected,
47-
'aria-valuemax': ariaValueMax,
48-
'aria-valuemin': ariaValueMin,
49-
'aria-valuenow': ariaValueNow,
50-
'aria-valuetext': ariaValueText,
51-
focusable,
52-
id,
53-
importantForAccessibility,
54-
nativeID,
55-
tabIndex,
56-
...otherProps
57-
}: ViewProps,
58-
forwardedRef,
59-
) => {
60-
const hasTextAncestor = React.useContext(TextAncestor);
61-
const _accessibilityLabelledBy =
62-
ariaLabelledBy?.split(/\s*,\s*/g) ?? accessibilityLabelledBy;
63-
64-
const _accessibilityState =
65-
accessibilityState != null ||
66-
ariaBusy != null ||
67-
ariaChecked != null ||
68-
ariaDisabled != null ||
69-
ariaExpanded != null ||
70-
ariaSelected != null
71-
? {
72-
busy: ariaBusy ?? accessibilityState?.busy,
73-
checked: ariaChecked ?? accessibilityState?.checked,
74-
disabled: ariaDisabled ?? accessibilityState?.disabled,
75-
expanded: ariaExpanded ?? accessibilityState?.expanded,
76-
selected: ariaSelected ?? accessibilityState?.selected,
77-
}
78-
: undefined;
54+
...ViewProps,
55+
}>): React.Node {
56+
const hasTextAncestor = React.use(TextAncestor);
57+
const _accessibilityLabelledBy =
58+
ariaLabelledBy?.split(/\s*,\s*/g) ?? accessibilityLabelledBy;
7959

80-
const _accessibilityValue =
81-
accessibilityValue != null ||
82-
ariaValueMax != null ||
83-
ariaValueMin != null ||
84-
ariaValueNow != null ||
85-
ariaValueText != null
86-
? {
87-
max: ariaValueMax ?? accessibilityValue?.max,
88-
min: ariaValueMin ?? accessibilityValue?.min,
89-
now: ariaValueNow ?? accessibilityValue?.now,
90-
text: ariaValueText ?? accessibilityValue?.text,
91-
}
92-
: undefined;
93-
94-
const actualView = (
95-
<ViewNativeComponent
96-
{...otherProps}
97-
accessibilityLiveRegion={
98-
ariaLive === 'off' ? 'none' : ariaLive ?? accessibilityLiveRegion
60+
const _accessibilityState =
61+
accessibilityState != null ||
62+
ariaBusy != null ||
63+
ariaChecked != null ||
64+
ariaDisabled != null ||
65+
ariaExpanded != null ||
66+
ariaSelected != null
67+
? {
68+
busy: ariaBusy ?? accessibilityState?.busy,
69+
checked: ariaChecked ?? accessibilityState?.checked,
70+
disabled: ariaDisabled ?? accessibilityState?.disabled,
71+
expanded: ariaExpanded ?? accessibilityState?.expanded,
72+
selected: ariaSelected ?? accessibilityState?.selected,
9973
}
100-
accessibilityLabel={ariaLabel ?? accessibilityLabel}
101-
focusable={tabIndex !== undefined ? !tabIndex : focusable}
102-
accessibilityState={_accessibilityState}
103-
accessibilityElementsHidden={ariaHidden ?? accessibilityElementsHidden}
104-
accessibilityLabelledBy={_accessibilityLabelledBy}
105-
accessibilityValue={_accessibilityValue}
106-
importantForAccessibility={
107-
ariaHidden === true
108-
? 'no-hide-descendants'
109-
: importantForAccessibility
74+
: undefined;
75+
76+
const _accessibilityValue =
77+
accessibilityValue != null ||
78+
ariaValueMax != null ||
79+
ariaValueMin != null ||
80+
ariaValueNow != null ||
81+
ariaValueText != null
82+
? {
83+
max: ariaValueMax ?? accessibilityValue?.max,
84+
min: ariaValueMin ?? accessibilityValue?.min,
85+
now: ariaValueNow ?? accessibilityValue?.now,
86+
text: ariaValueText ?? accessibilityValue?.text,
11087
}
111-
nativeID={id ?? nativeID}
112-
ref={forwardedRef}
113-
/>
114-
);
88+
: undefined;
11589

116-
if (hasTextAncestor) {
117-
return (
118-
<TextAncestor.Provider value={false}>
119-
{actualView}
120-
</TextAncestor.Provider>
121-
);
122-
}
90+
const actualView = (
91+
<ViewNativeComponent
92+
{...otherProps}
93+
accessibilityLiveRegion={
94+
ariaLive === 'off' ? 'none' : ariaLive ?? accessibilityLiveRegion
95+
}
96+
accessibilityLabel={ariaLabel ?? accessibilityLabel}
97+
focusable={tabIndex !== undefined ? !tabIndex : focusable}
98+
accessibilityState={_accessibilityState}
99+
accessibilityElementsHidden={ariaHidden ?? accessibilityElementsHidden}
100+
accessibilityLabelledBy={_accessibilityLabelledBy}
101+
accessibilityValue={_accessibilityValue}
102+
importantForAccessibility={
103+
ariaHidden === true ? 'no-hide-descendants' : importantForAccessibility
104+
}
105+
nativeID={id ?? nativeID}
106+
/>
107+
);
123108

124-
return actualView;
125-
},
126-
);
109+
if (hasTextAncestor) {
110+
return <TextAncestor value={false}>{actualView}</TextAncestor>;
111+
}
127112

128-
View.displayName = 'View';
113+
return actualView;
114+
}
129115

130-
export default View;
116+
export default View as component(
117+
ref?: React.RefSetter<React.ElementRef<typeof ViewNativeComponent>>,
118+
...props: ViewProps
119+
);

packages/react-native/Libraries/Components/View/__tests__/View-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ describe('View', () => {
2525
});
2626

2727
it('has displayName', () => {
28-
expect(View.displayName).toEqual('View');
28+
expect(View.displayName ?? View.name).toEqual('View');
2929
});
3030
});
3131

packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3496,11 +3496,10 @@ declare export default typeof ReactNativeViewAttributes;
34963496

34973497
exports[`public API should not change unintentionally Libraries/Components/View/View.js 1`] = `
34983498
"export type Props = ViewProps;
3499-
declare const View: component(
3499+
declare export default component(
35003500
ref?: React.RefSetter<React.ElementRef<typeof ViewNativeComponent>>,
35013501
...props: ViewProps
35023502
);
3503-
declare export default typeof View;
35043503
"
35053504
`;
35063505

0 commit comments

Comments
 (0)