Repository navigation
[Release] 2.31.2 cherry-pick thread - #4142
Merged
Merged
Conversation
## Description I've noticed that `Pinch` and `Rotation` behave weirdly when it comes to ending gesture on web. I've took a look and fixed some minor issues. > [!NOTE] > This PR may introduce mismatch between `android` and `web` versions of `Rotation`. I still think that this is how it is supposed to work, so we may want to [make required changes to `android` as well](#4079) (or drop this PR if you prefer to). ### Pinch I've noticed that `Pinch` is not reset when pointers are placed on view and then released. This was because for some reason `Pinch` simply ignored finished states if it had not activated earlier. ### Rotation `Rotation`, always finalized with `true`. This was because we always tried to move it to `end` state. Our internal event handler hadn't been sending `onDeactivate`, but `true` was still passed into `onFinalize`. ## Test plan <details> <summary>Tested on Transformations example and code below:</summary> ```tsx import { StyleSheet, View } from 'react-native'; import { GestureDetector, GestureHandlerRootView, usePinchGesture, useRotationGesture, } from 'react-native-gesture-handler'; export default function App() { const g = useRotationGesture({ onBegin: () => console.log('onBegin'), onActivate: () => console.log('onActivate'), onUpdate: (e) => console.log('onUpdate', e.rotation), onDeactivate: () => console.log('onDeactivate'), onFinalize: (_, s) => console.log('onFinalize', s), }); return ( <GestureHandlerRootView style={styles.container}> <GestureDetector gesture={g}> <View style={styles.box} /> </GestureDetector> </GestureHandlerRootView> ); } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', }, box: { width: 200, height: 200, backgroundColor: 'blue', borderRadius: 12, }, }); ``` </details>
## Description Follow up for #4078 I've noticed that when `Rotation` does not activate, but pointers are released, `onFinalize` is called with `true`. This happens because we try to move it to `END` state, but our internal logic does not send `onDeactivate`. Code in `onHandle` that I removed was effectively dead, as `ACTION_UP` was handled in rotation detector - the only possible branch was `else` statement which fails handler if only one pointer was present. ## Test plan <details> <summary>Tested on Transformations example and the following code:</summary> ```tsx import { StyleSheet, View } from 'react-native'; import { GestureDetector, GestureHandlerRootView, usePinchGesture, useRotationGesture, } from 'react-native-gesture-handler'; export default function App() { const g = useRotationGesture({ onBegin: () => console.log('onBegin'), onActivate: () => console.log('onActivate'), onUpdate: (e) => console.log('onUpdate', e.rotation), onDeactivate: () => console.log('onDeactivate'), onFinalize: (_, s) => console.log('onFinalize', s), }); return ( <GestureHandlerRootView style={styles.container}> <GestureDetector gesture={g}> <View style={styles.box} /> </GestureDetector> </GestureHandlerRootView> ); } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', }, box: { width: 200, height: 200, backgroundColor: 'blue', borderRadius: 12, }, }); ``` </details>
# Fix ReanimatedSwipeable ref type to support ref callbacks
## Description
This PR fixes the `ref` prop type in `ReanimatedSwipeableProps` to
support both ref objects and ref callbacks, improving TypeScript
compatibility and developer experience.
## Changes
- Changed `ref?: React.RefObject<SwipeableMethods | null>` to `ref?:
React.Ref<SwipeableMethods | null>` in `ReanimatedSwipeableProps`
interface
## Motivation
The current implementation only supports `React.RefObject` which limits
developers to using `useRef()` hook. By changing to `React.Ref`, the
component now supports:
1. **Ref objects** (`useRef()`)
2. **Ref callbacks** (function refs)
This change aligns with React's best practices and provides more
flexibility for developers when working with refs.
## Example Usage
### Before (only ref objects)
```tsx
const swipeableRef = useRef<SwipeableMethods>(null);
<ReanimatedSwipeable ref={swipeableRef}>
{/* content */}
</ReanimatedSwipeable>
```
### After (supports both ref objects and callbacks)
```tsx
// Ref object
const swipeableRef = useRef<SwipeableMethods>(null);
// Ref callback
const handleRef = (ref: SwipeableMethods | null) => {
// Custom logic
};
<ReanimatedSwipeable ref={swipeableRef}>
{/* content */}
</ReanimatedSwipeable>
<ReanimatedSwipeable ref={handleRef}>
{/* content */}
</ReanimatedSwipeable>
```
## Type Safety
This change maintains full TypeScript type safety while expanding the
supported ref patterns. The `React.Ref<T>` type is a union type that
includes:
- `React.RefObject<T>`
- `React.RefCallback<T>`
## Breaking Changes
None. This is a backward-compatible change that only expands the
supported ref types.
## Testing
- [x] Existing tests pass
- [x] TypeScript compilation succeeds
- [x] Both ref object and ref callback patterns work correctly
## Related Issues
Fixes the limitation where developers could only use `useRef()` with
ReanimatedSwipeable components.
---------
Co-authored-by: Michał <michal.bert@swmansion.com>
Co-authored-by: Michał Bert <63123542+m-bert@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
## Description In some environments babel plugin is not used (e.g. it can be omitted when using `Next.js`). Our detector checks if callbacks are `worklets` by checking `__workletHash` field. However, if plugin wasn't used, this will be `undefined`. In that case, our warning will wrongly suggest to use `runOnJS`. This may be misleading, if plugin was skipped on `web`, but used on native platforms. In this PR, we check if plugin was applied by checking empty worklet function. If it has `__workletHash` field it means that plugin was used. If it wasn't, we won't suggest using `runOnJS`. ## Test plan Confirmed by the original reporter
When TalkBack is activated, pressable cannot be clicked on android. This
PR fixes the issue.
The reason was that Pressable requires a specific order of events. This
order of events is hardcoded and found empirically. However, with
talback enabled the order of events is different, I added a function
which queries native side whether or not talback is enabled and adjusts
expected event order accordingly.
Moreover with talback enabled longPress gets both onPointerUp and
onPointerDown before native side receives its events, thus we must not
reset the state machine state on onPointerUp.
Tested on the following example:
<details>
```tsx
import React from 'react';
import { Text, StyleSheet, View } from 'react-native';
import { Pressable, GestureHandlerRootView } from 'react-native-gesture-handler'
const PressableExample = () => {
const [count, setCount] = React.useState(0);
return (
<GestureHandlerRootView style={styles.container}>
<Pressable onPress={() => setCount((c) => c + 1)}>
<View style={styles.pressable}>
<Text>{count}</Text>
</View>
</Pressable>
</GestureHandlerRootView >
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
pressable: {
alignItems: 'center', justifyContent: 'center', backgroundColor: 'green', width: 100, height: 100
},
wrapperCustom: {
borderRadius: 8,
padding: 16,
minWidth: 150,
alignItems: 'center',
},
text: {
fontSize: 18,
color: 'white',
fontWeight: '600',
},
});
export default PressableExample;
```
</details>
j-piasecki
approved these changes
May 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Cherry pick thread for release 2.31.2
List of PRs
PinchandRotation#4078Rotationhandler #4079workletsif babel plugin was not used #4107Conflicts
PressableTest plan
🚀