diff --git a/packages/react-native/Libraries/Pressability/usePressability.js b/packages/react-native/Libraries/Pressability/usePressability.js index 3bc8820f5711c7..791c54dfda81c5 100644 --- a/packages/react-native/Libraries/Pressability/usePressability.js +++ b/packages/react-native/Libraries/Pressability/usePressability.js @@ -8,26 +8,15 @@ * @format */ -import * as ReactNativeFeatureFlags from '../../src/private/featureflags/ReactNativeFeatureFlags'; import Pressability, { type EventHandlers, type PressabilityConfig, } from './Pressability'; -import {useEffect, useInsertionEffect, useRef} from 'react'; +import {useInsertionEffect, useRef} from 'react'; declare function usePressability(config: PressabilityConfig): EventHandlers; declare function usePressability(config: null | void): null | EventHandlers; -// Experiments with using `useInsertionEffect` instead of `useEffect`, which -// changes whether `Pressability` is configured or reset when inm a hidden -// Activity. With `useInsertionEffect`, `Pressability` behaves more like a -// platform control (e.g. Pointer Events), especially with respect to events -// like focus and blur. -const useConfigurationEffect = - ReactNativeFeatureFlags.configurePressabilityDuringInsertion() - ? useInsertionEffect - : useEffect; - /** * Creates a persistent instance of `Pressability` that automatically configures * itself and resets. Accepts null `config` to support lazy initialization. Once @@ -51,7 +40,7 @@ export default function usePressability( // On the initial mount, this is a no-op. On updates, `pressability` will be // re-configured to use the new configuration. - useConfigurationEffect(() => { + useInsertionEffect(() => { if (config != null && pressability != null) { pressability.configure(config); } @@ -59,7 +48,7 @@ export default function usePressability( // On unmount, reset pending state and timers inside `pressability`. This is // a separate effect because we do not want to reset when `config` changes. - useConfigurationEffect(() => { + useInsertionEffect(() => { if (pressability != null) { return () => { pressability.reset(); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 5c58fa41b30a65..bb571572fece29 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -989,17 +989,6 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, - configurePressabilityDuringInsertion: { - defaultValue: false, - metadata: { - dateAdded: '2025-10-27', - description: - 'Configure Pressability during insertion and no longer unmount when hidden.', - expectedReleaseValue: true, - purpose: 'experimentation', - }, - ossReleaseStage: 'none', - }, deferFlatListFocusChangeRenderUpdate: { defaultValue: false, metadata: { @@ -1022,16 +1011,6 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, - enableVirtualViewExperimental: { - defaultValue: false, - metadata: { - dateAdded: '2025-08-29', - description: 'Enables the experimental version of `VirtualView`.', - expectedReleaseValue: true, - purpose: 'experimentation', - }, - ossReleaseStage: 'none', - }, fixVirtualizeListCollapseWindowSize: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/components/virtualview/VirtualView.js b/packages/react-native/src/private/components/virtualview/VirtualView.js index c57c9cdfc2b82f..e83ce86eaae84e 100644 --- a/packages/react-native/src/private/components/virtualview/VirtualView.js +++ b/packages/react-native/src/private/components/virtualview/VirtualView.js @@ -11,13 +11,12 @@ import type {ViewStyleProp} from '../../../../Libraries/StyleSheet/StyleSheet'; import type {NativeSyntheticEvent} from '../../../../Libraries/Types/CoreEventTypes'; import type {HostInstance} from '../../types/HostInstance'; -import type {NativeModeChangeEvent} from './VirtualViewNativeComponent'; +import type {NativeModeChangeEvent} from './VirtualViewExperimentalNativeComponent'; import StyleSheet from '../../../../Libraries/StyleSheet/StyleSheet'; import * as ReactNativeFeatureFlags from '../../featureflags/ReactNativeFeatureFlags'; import {useVirtualViewLogging} from './logger/VirtualViewLogger'; -import VirtualViewExperimentalNativeComponent from './VirtualViewExperimentalNativeComponent'; -import VirtualViewClassicNativeComponent from './VirtualViewNativeComponent'; +import VirtualViewNativeComponent from './VirtualViewExperimentalNativeComponent'; import nullthrows from 'nullthrows'; import * as React from 'react'; // $FlowFixMe[missing-export] @@ -51,11 +50,6 @@ export type ModeChangeEvent = $ReadOnly<{ target: HostInstance, }>; -const VirtualViewNativeComponent: typeof VirtualViewClassicNativeComponent = - ReactNativeFeatureFlags.enableVirtualViewExperimental() - ? VirtualViewExperimentalNativeComponent - : VirtualViewClassicNativeComponent; - type VirtualViewComponent = component( children?: React.Node, hiddenStyle?: (targetRect: Rect) => ViewStyleProp, diff --git a/packages/react-native/src/private/components/virtualview/VirtualViewExperimentalNativeComponent.js b/packages/react-native/src/private/components/virtualview/VirtualViewExperimentalNativeComponent.js index 81d54ad8d743fb..fa980e807481ed 100644 --- a/packages/react-native/src/private/components/virtualview/VirtualViewExperimentalNativeComponent.js +++ b/packages/react-native/src/private/components/virtualview/VirtualViewExperimentalNativeComponent.js @@ -67,6 +67,12 @@ type VirtualViewExperimentalNativeProps = $ReadOnly<{ */ initialHidden?: boolean, + /** + * This was needed to get VirtualViewManagerDelegate to set this property. + * TODO: Investigate why spread ViewProps doesn't call setter + */ + removeClippedSubviews?: boolean, + /** * Render state of children. * @@ -79,18 +85,13 @@ type VirtualViewExperimentalNativeProps = $ReadOnly<{ */ renderState: Int32, - /** - * This was needed to get VirtualViewManagerDelegate to set this property. - * TODO: Investigate why spread ViewProps doesn't call setter - */ - removeClippedSubviews?: boolean, - /** * See `NativeModeChangeEvent`. */ onModeChange?: ?DirectEventHandler, }>; +// TODO: Rename to eliminate "Experimental" suffix in the name. export default codegenNativeComponent( 'VirtualViewExperimental', { diff --git a/packages/react-native/src/private/components/virtualview/__tests__/VirtualView-itest.js b/packages/react-native/src/private/components/virtualview/__tests__/VirtualView-itest.js index 93792282b67ebc..b05e23c2f42d14 100644 --- a/packages/react-native/src/private/components/virtualview/__tests__/VirtualView-itest.js +++ b/packages/react-native/src/private/components/virtualview/__tests__/VirtualView-itest.js @@ -11,7 +11,7 @@ import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment'; import type {Rect} from '../VirtualView'; -import type {NativeModeChangeEvent} from '../VirtualViewNativeComponent'; +import type {NativeModeChangeEvent} from '../VirtualViewExperimentalNativeComponent'; import ensureInstance from '../../../__tests__/utilities/ensureInstance'; import isUnreachable from '../../../__tests__/utilities/isUnreachable'; @@ -43,16 +43,16 @@ describe('mode changes', () => { expect(_logs.states).toHaveLength(1); expect(root.getRenderedOutput({props: []}).toJSX()).toEqual( - + Child - , + , ); dispatchModeChangeEvent(viewRef.current, VirtualViewMode.Hidden); expect(_logs.states).toHaveLength(2); expect(root.getRenderedOutput({props: []}).toJSX()).toEqual( - , + , ); }); @@ -72,16 +72,16 @@ describe('mode changes', () => { expect(_logs.states).toHaveLength(2); expect(root.getRenderedOutput({props: []}).toJSX()).toEqual( - , + , ); dispatchModeChangeEvent(viewRef.current, VirtualViewMode.Visible); expect(_logs.states).toHaveLength(3); expect(root.getRenderedOutput({props: []}).toJSX()).toEqual( - + Child - , + , ); }); @@ -101,9 +101,9 @@ describe('mode changes', () => { expect(_logs.states).toHaveLength(1); expect(root.getRenderedOutput({props: []}).toJSX()).toEqual( - + Child - , + , ); dispatchModeChangeEvent(viewRef.current, VirtualViewMode.Visible); @@ -111,9 +111,9 @@ describe('mode changes', () => { // Expects `VirtualView` does not undergo a state update. expect(_logs.states).toHaveLength(1); expect(root.getRenderedOutput({props: []}).toJSX()).toEqual( - + Child - , + , ); }); }); @@ -128,7 +128,7 @@ describe('styles', () => { expect( root.getRenderedOutput({props: ['minHeight', 'minWidth']}).toJSX(), - ).toEqual(); + ).toEqual(); }); test('does not set styles when prerendered', () => { @@ -143,7 +143,7 @@ describe('styles', () => { expect( root.getRenderedOutput({props: ['minHeight', 'minWidth']}).toJSX(), - ).toEqual(); + ).toEqual(); }); test('sets styles when hidden', () => { @@ -158,7 +158,12 @@ describe('styles', () => { expect( root.getRenderedOutput({props: ['minHeight', 'minWidth']}).toJSX(), - ).toEqual(); + ).toEqual( + , + ); }); }); diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index cd5a09ee4c7f4e..f7c868b13b5c95 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<43e6a2dddd5965011ba166098f90e33f>> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -31,10 +31,8 @@ export type ReactNativeFeatureFlagsJsOnly = $ReadOnly<{ jsOnlyTestFlag: Getter, animatedShouldDebounceQueueFlush: Getter, animatedShouldUseSingleOp: Getter, - configurePressabilityDuringInsertion: Getter, deferFlatListFocusChangeRenderUpdate: Getter, disableMaintainVisibleContentPosition: Getter, - enableVirtualViewExperimental: Getter, fixVirtualizeListCollapseWindowSize: Getter, isLayoutAnimationEnabled: Getter, reduceDefaultPropsInImage: Getter, @@ -154,11 +152,6 @@ export const animatedShouldDebounceQueueFlush: Getter = createJavaScrip */ export const animatedShouldUseSingleOp: Getter = createJavaScriptFlagGetter('animatedShouldUseSingleOp', false); -/** - * Configure Pressability during insertion and no longer unmount when hidden. - */ -export const configurePressabilityDuringInsertion: Getter = createJavaScriptFlagGetter('configurePressabilityDuringInsertion', false); - /** * Use the deferred cell render update mechanism for focus change in FlatList. */ @@ -169,11 +162,6 @@ export const deferFlatListFocusChangeRenderUpdate: Getter = createJavaS */ export const disableMaintainVisibleContentPosition: Getter = createJavaScriptFlagGetter('disableMaintainVisibleContentPosition', false); -/** - * Enables the experimental version of `VirtualView`. - */ -export const enableVirtualViewExperimental: Getter = createJavaScriptFlagGetter('enableVirtualViewExperimental', false); - /** * Fixing an edge case where the current window size is not properly calculated with fast scrolling. Window size collapsed to 1 element even if windowSize more than the current amount of elements */