Skip to content

Commit c079c77

Browse files
latekvom-bert
authored andcommitted
Fix Pressable requiring dimensionsAfterResize (#3606)
## Description The `Pressable` was developed with an incorrect assumption that the `onLayout` prop is not available on the `NativeButton`. This PR defines the `onLayout` on `RawButtonProps`, and makes use of said prop in `Pressable` to remove the need for the `dimensionsAfterResize` property. This PR also marks `dimensionsAfterResize` as deprecated. Fixes #3600 ## Test plan 1. Use the provided test code. 2. Notice how the `Pressable`, despite starting out with `0, 0` dimensions, responds correctly to all press events. 3. Use the `Pressable` examples in our example app to confirm there are no new issues with the component. ## Test code <details> ```tsx import React, { useEffect } from 'react'; import { StyleSheet } from 'react-native'; import { Pressable, GestureHandlerRootView, } from 'react-native-gesture-handler'; import Animated, { useSharedValue, useAnimatedStyle, withTiming, } from 'react-native-reanimated'; export default function EmptyExample() { const opacity = useSharedValue(0); const containerAnimatedStyle = useAnimatedStyle(() => { 'worklet'; return { opacity: opacity.value, transform: [{ scale: opacity.value }], }; }); useEffect(() => { opacity.value = withTiming(1, { duration: 200 }); }, [opacity]); return ( <GestureHandlerRootView style={styles.container}> <Animated.View style={containerAnimatedStyle}> <Pressable style={styles.box} onPress={() => console.log('press')} /> </Animated.View> </GestureHandlerRootView> ); } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 10, }, box: { width: 150, height: 150, backgroundColor: 'red', alignItems: 'center', justifyContent: 'center', }, }); ``` </details> --------- Co-authored-by: Michał Bert <[email protected]>
1 parent 9f33d64 commit c079c77

File tree

3 files changed

+19
-27
lines changed

3 files changed

+19
-27
lines changed

packages/react-native-gesture-handler/src/components/GestureButtonsProps.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as React from 'react';
22
import {
33
AccessibilityProps,
44
ColorValue,
5+
LayoutChangeEvent,
56
StyleProp,
67
ViewStyle,
78
} from 'react-native';
@@ -56,6 +57,11 @@ export interface RawButtonProps
5657
*/
5758
style?: StyleProp<ViewStyle>;
5859

60+
/**
61+
* Invoked on mount and layout changes.
62+
*/
63+
onLayout?: (event: LayoutChangeEvent) => void;
64+
5965
/**
6066
* Used for testing-library compatibility, not passed to the native component.
6167
*/

packages/react-native-gesture-handler/src/components/Pressable/Pressable.tsx

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React, {
22
useCallback,
33
useEffect,
4-
useLayoutEffect,
54
useMemo,
65
useRef,
76
useState,
@@ -15,9 +14,9 @@ import {
1514
} from './PressableProps';
1615
import {
1716
Insets,
17+
LayoutChangeEvent,
1818
Platform,
1919
StyleProp,
20-
View,
2120
ViewStyle,
2221
processColor,
2322
} from 'react-native';
@@ -46,7 +45,6 @@ let IS_FABRIC: null | boolean = null;
4645

4746
const Pressable = (props: PressableProps) => {
4847
const {
49-
ref,
5048
testOnly_pressed,
5149
hitSlop,
5250
pressRetentionOffset,
@@ -60,6 +58,7 @@ const Pressable = (props: PressableProps) => {
6058
onPressIn,
6159
onPressOut,
6260
onLongPress,
61+
onLayout,
6362
style,
6463
children,
6564
android_disableSound,
@@ -69,7 +68,6 @@ const Pressable = (props: PressableProps) => {
6968
simultaneousWithExternalGesture,
7069
requireExternalGestureToFail,
7170
blocksExternalGesture,
72-
dimensionsAfterResize,
7371
...remainingProps
7472
} = props;
7573

@@ -79,9 +77,6 @@ const Pressable = (props: PressableProps) => {
7977
blocksExternalGesture,
8078
};
8179

82-
// used only if `ref` is undefined
83-
const fallbackRef = useRef<View>(null);
84-
8580
const [pressedState, setPressedState] = useState(testOnly_pressed ?? false);
8681

8782
const longPressTimeoutRef = useRef<number | null>(null);
@@ -109,21 +104,6 @@ const Pressable = (props: PressableProps) => {
109104
normalizedPressRetentionOffset
110105
);
111106

112-
useLayoutEffect(() => {
113-
if (dimensionsAfterResize) {
114-
dimensions.current = dimensionsAfterResize;
115-
} else {
116-
requestAnimationFrame(() => {
117-
(ref ?? fallbackRef).current?.measure((_x, _y, width, height) => {
118-
dimensions.current = {
119-
width,
120-
height,
121-
};
122-
});
123-
});
124-
}
125-
}, [dimensionsAfterResize, ref]);
126-
127107
const cancelLongPress = useCallback(() => {
128108
if (longPressTimeoutRef.current) {
129109
clearTimeout(longPressTimeoutRef.current);
@@ -377,11 +357,19 @@ const Pressable = (props: PressableProps) => {
377357
: processColor(unprocessedRippleColor);
378358
}, [android_ripple]);
379359

360+
const setDimensions = useCallback(
361+
(event: LayoutChangeEvent) => {
362+
onLayout?.(event);
363+
dimensions.current = event.nativeEvent.layout;
364+
},
365+
[onLayout]
366+
);
367+
380368
return (
381369
<GestureDetector gesture={gesture}>
382370
<NativeButton
383371
{...remainingProps}
384-
ref={ref ?? fallbackRef}
372+
onLayout={setDimensions}
385373
accessible={accessible !== false}
386374
hitSlop={appliedHitSlop}
387375
enabled={isPressableEnabled}

packages/react-native-gesture-handler/src/components/Pressable/PressableProps.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export interface PressableProps
6666
/**
6767
* A reference to the pressable element.
6868
*/
69-
ref?: React.RefObject<View>;
69+
ref?: React.Ref<View>;
7070

7171
/**
7272
* Either children or a render prop that receives a boolean reflecting whether
@@ -168,9 +168,7 @@ export interface PressableProps
168168
blocksExternalGesture?: RelationPropType;
169169

170170
/**
171-
* Defines the dimensions of the Pressable after it's been resized.
172-
* This property does not affect Pressable's physical appearance.
173-
* Required when the Pressable is resized **and** uses pressRetentionOffset.
171+
* @deprecated This property is no longer used, and will be removed in the future.
174172
*/
175173
dimensionsAfterResize?: PressableDimensions;
176174
}

0 commit comments

Comments
 (0)