mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
chore: convert View to React 19 (#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: https://github.com/facebook/react-native/pull/51023 Test Plan: - Type checks should pass. Reviewed By: rshest Differential Revision: D74546184 Pulled By: yungsters fbshipit-source-id: b8257e3a75477c1117b19cd3f8e0843947b092ca
This commit is contained in:
committed by
Facebook GitHub Bot
parent
5d7f35cd7c
commit
eedd60b9e6
+91
-102
@@ -23,108 +23,97 @@ export type Props = ViewProps;
|
||||
*
|
||||
* @see https://reactnative.dev/docs/view
|
||||
*/
|
||||
const View: component(
|
||||
function View({
|
||||
accessibilityElementsHidden,
|
||||
accessibilityLabel,
|
||||
accessibilityLabelledBy,
|
||||
accessibilityLiveRegion,
|
||||
accessibilityState,
|
||||
accessibilityValue,
|
||||
'aria-busy': ariaBusy,
|
||||
'aria-checked': ariaChecked,
|
||||
'aria-disabled': ariaDisabled,
|
||||
'aria-expanded': ariaExpanded,
|
||||
'aria-hidden': ariaHidden,
|
||||
'aria-label': ariaLabel,
|
||||
'aria-labelledby': ariaLabelledBy,
|
||||
'aria-live': ariaLive,
|
||||
'aria-selected': ariaSelected,
|
||||
'aria-valuemax': ariaValueMax,
|
||||
'aria-valuemin': ariaValueMin,
|
||||
'aria-valuenow': ariaValueNow,
|
||||
'aria-valuetext': ariaValueText,
|
||||
focusable,
|
||||
id,
|
||||
importantForAccessibility,
|
||||
nativeID,
|
||||
tabIndex,
|
||||
...otherProps
|
||||
}: $ReadOnly<{
|
||||
ref?: React.RefSetter<React.ElementRef<typeof ViewNativeComponent>>,
|
||||
...ViewProps,
|
||||
}>): React.Node {
|
||||
const hasTextAncestor = React.use(TextAncestor);
|
||||
const _accessibilityLabelledBy =
|
||||
ariaLabelledBy?.split(/\s*,\s*/g) ?? accessibilityLabelledBy;
|
||||
|
||||
const _accessibilityState =
|
||||
accessibilityState != null ||
|
||||
ariaBusy != null ||
|
||||
ariaChecked != null ||
|
||||
ariaDisabled != null ||
|
||||
ariaExpanded != null ||
|
||||
ariaSelected != null
|
||||
? {
|
||||
busy: ariaBusy ?? accessibilityState?.busy,
|
||||
checked: ariaChecked ?? accessibilityState?.checked,
|
||||
disabled: ariaDisabled ?? accessibilityState?.disabled,
|
||||
expanded: ariaExpanded ?? accessibilityState?.expanded,
|
||||
selected: ariaSelected ?? accessibilityState?.selected,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const _accessibilityValue =
|
||||
accessibilityValue != null ||
|
||||
ariaValueMax != null ||
|
||||
ariaValueMin != null ||
|
||||
ariaValueNow != null ||
|
||||
ariaValueText != null
|
||||
? {
|
||||
max: ariaValueMax ?? accessibilityValue?.max,
|
||||
min: ariaValueMin ?? accessibilityValue?.min,
|
||||
now: ariaValueNow ?? accessibilityValue?.now,
|
||||
text: ariaValueText ?? accessibilityValue?.text,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const actualView = (
|
||||
<ViewNativeComponent
|
||||
{...otherProps}
|
||||
accessibilityLiveRegion={
|
||||
ariaLive === 'off' ? 'none' : ariaLive ?? accessibilityLiveRegion
|
||||
}
|
||||
accessibilityLabel={ariaLabel ?? accessibilityLabel}
|
||||
focusable={tabIndex !== undefined ? !tabIndex : focusable}
|
||||
accessibilityState={_accessibilityState}
|
||||
accessibilityElementsHidden={ariaHidden ?? accessibilityElementsHidden}
|
||||
accessibilityLabelledBy={_accessibilityLabelledBy}
|
||||
accessibilityValue={_accessibilityValue}
|
||||
importantForAccessibility={
|
||||
ariaHidden === true ? 'no-hide-descendants' : importantForAccessibility
|
||||
}
|
||||
nativeID={id ?? nativeID}
|
||||
/>
|
||||
);
|
||||
|
||||
if (hasTextAncestor) {
|
||||
return <TextAncestor value={false}>{actualView}</TextAncestor>;
|
||||
}
|
||||
|
||||
return actualView;
|
||||
}
|
||||
|
||||
export default View as component(
|
||||
ref?: React.RefSetter<React.ElementRef<typeof ViewNativeComponent>>,
|
||||
...props: ViewProps
|
||||
) = React.forwardRef(
|
||||
(
|
||||
{
|
||||
accessibilityElementsHidden,
|
||||
accessibilityLabel,
|
||||
accessibilityLabelledBy,
|
||||
accessibilityLiveRegion,
|
||||
accessibilityState,
|
||||
accessibilityValue,
|
||||
'aria-busy': ariaBusy,
|
||||
'aria-checked': ariaChecked,
|
||||
'aria-disabled': ariaDisabled,
|
||||
'aria-expanded': ariaExpanded,
|
||||
'aria-hidden': ariaHidden,
|
||||
'aria-label': ariaLabel,
|
||||
'aria-labelledby': ariaLabelledBy,
|
||||
'aria-live': ariaLive,
|
||||
'aria-selected': ariaSelected,
|
||||
'aria-valuemax': ariaValueMax,
|
||||
'aria-valuemin': ariaValueMin,
|
||||
'aria-valuenow': ariaValueNow,
|
||||
'aria-valuetext': ariaValueText,
|
||||
focusable,
|
||||
id,
|
||||
importantForAccessibility,
|
||||
nativeID,
|
||||
tabIndex,
|
||||
...otherProps
|
||||
}: ViewProps,
|
||||
forwardedRef,
|
||||
) => {
|
||||
const hasTextAncestor = React.useContext(TextAncestor);
|
||||
const _accessibilityLabelledBy =
|
||||
ariaLabelledBy?.split(/\s*,\s*/g) ?? accessibilityLabelledBy;
|
||||
|
||||
const _accessibilityState =
|
||||
accessibilityState != null ||
|
||||
ariaBusy != null ||
|
||||
ariaChecked != null ||
|
||||
ariaDisabled != null ||
|
||||
ariaExpanded != null ||
|
||||
ariaSelected != null
|
||||
? {
|
||||
busy: ariaBusy ?? accessibilityState?.busy,
|
||||
checked: ariaChecked ?? accessibilityState?.checked,
|
||||
disabled: ariaDisabled ?? accessibilityState?.disabled,
|
||||
expanded: ariaExpanded ?? accessibilityState?.expanded,
|
||||
selected: ariaSelected ?? accessibilityState?.selected,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const _accessibilityValue =
|
||||
accessibilityValue != null ||
|
||||
ariaValueMax != null ||
|
||||
ariaValueMin != null ||
|
||||
ariaValueNow != null ||
|
||||
ariaValueText != null
|
||||
? {
|
||||
max: ariaValueMax ?? accessibilityValue?.max,
|
||||
min: ariaValueMin ?? accessibilityValue?.min,
|
||||
now: ariaValueNow ?? accessibilityValue?.now,
|
||||
text: ariaValueText ?? accessibilityValue?.text,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const actualView = (
|
||||
<ViewNativeComponent
|
||||
{...otherProps}
|
||||
accessibilityLiveRegion={
|
||||
ariaLive === 'off' ? 'none' : ariaLive ?? accessibilityLiveRegion
|
||||
}
|
||||
accessibilityLabel={ariaLabel ?? accessibilityLabel}
|
||||
focusable={tabIndex !== undefined ? !tabIndex : focusable}
|
||||
accessibilityState={_accessibilityState}
|
||||
accessibilityElementsHidden={ariaHidden ?? accessibilityElementsHidden}
|
||||
accessibilityLabelledBy={_accessibilityLabelledBy}
|
||||
accessibilityValue={_accessibilityValue}
|
||||
importantForAccessibility={
|
||||
ariaHidden === true
|
||||
? 'no-hide-descendants'
|
||||
: importantForAccessibility
|
||||
}
|
||||
nativeID={id ?? nativeID}
|
||||
ref={forwardedRef}
|
||||
/>
|
||||
);
|
||||
|
||||
if (hasTextAncestor) {
|
||||
return (
|
||||
<TextAncestor.Provider value={false}>
|
||||
{actualView}
|
||||
</TextAncestor.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
return actualView;
|
||||
},
|
||||
);
|
||||
|
||||
View.displayName = 'View';
|
||||
|
||||
export default View;
|
||||
|
||||
@@ -25,7 +25,7 @@ describe('View', () => {
|
||||
});
|
||||
|
||||
it('has displayName', () => {
|
||||
expect(View.displayName).toEqual('View');
|
||||
expect(View.displayName ?? View.name).toEqual('View');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -3496,11 +3496,10 @@ declare export default typeof ReactNativeViewAttributes;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/Components/View/View.js 1`] = `
|
||||
"export type Props = ViewProps;
|
||||
declare const View: component(
|
||||
declare export default component(
|
||||
ref?: React.RefSetter<React.ElementRef<typeof ViewNativeComponent>>,
|
||||
...props: ViewProps
|
||||
);
|
||||
declare export default typeof View;
|
||||
"
|
||||
`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user