You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix GestureDetector not working when the underlying view changes (#2092)
## Description
`GestureDetector` was not reattaching gestures if the underlying view
has changed, which was especially noticeable when using layout
animations.
This PR updates `GestureDetector` to keep track of the tag of the view
it's attached to and to reattach gestures it the tag changes.
The second commit also fixes gestures not reattaching when manually
changing the underlying view (at the expense of forcing another render),
but only when Reanimated is not used.
Applying the following patch:
<details>
<summary>Expand</summary>
```diff
diff --git a/node_modules/react-native-reanimated/src/createAnimatedComponent.tsx b/node_modules/react-native-reanimated/src/createAnimatedComponent.tsx
index 1cf0c3f..3f22437 100644
--- a/node_modules/react-native-reanimated/src/createAnimatedComponent.tsx
+++ b/node_modules/react-native-reanimated/src/createAnimatedComponent.tsx
@@ -294,19 +294,12 @@ export default function createAnimatedComponent(
const node = this._getEventViewRef();
const attached = new Set();
const nextEvts = new Set();
- let viewTag: number | undefined;
+ let viewTag: number | undefined = RNRenderer.findHostInstance_DEPRECATED(this)._nativeTag;
for (const key in this.props) {
const prop = this.props[key];
if (prop instanceof AnimatedEvent) {
nextEvts.add((prop as AnimatedEvent).__nodeID);
- } else if (
- has('current', prop) &&
- prop.current instanceof WorkletEventHandler
- ) {
- if (viewTag === undefined) {
- viewTag = prop.current.viewTag;
- }
}
}
for (const key in prevProps) {
```
</details>
also makes it work when using Reanimated, but I'm not sure whether it's
fine to change it this way upstream. This needs to be discussed.
## Test plan
Tested on the Example app and on the following code:
<details>
<summary>Expand</summary>
```jsx
import React, { useState } from 'react';
import { Text, View } from 'react-native';
import {
FlatList,
Gesture,
GestureDetector,
} from 'react-native-gesture-handler';
import Animated, { BounceIn } from 'react-native-reanimated';
const items = [
{ name: 'Item A' },
{ name: 'Item B' },
{ name: 'Item C' },
{ name: 'Item D' },
{ name: 'Item A' },
{ name: 'Item B' },
{ name: 'Item C' },
{ name: 'Item D' },
{ name: 'Item A' },
{ name: 'Item B' },
{ name: 'Item C' },
{ name: 'Item D' },
{ name: 'Item A' },
{ name: 'Item B' },
{ name: 'Item C' },
{ name: 'Item D' },
{ name: 'Item A' },
{ name: 'Item B' },
{ name: 'Item C' },
{ name: 'Item D' },
];
function Item() {
const [faved, setFaved] = useState(false);
const color = faved ? '#900' : '#aaa';
const tap = Gesture.Tap()
.onEnd(() => {
setFaved(!faved);
})
.runOnJS(true);
return (
<GestureDetector gesture={tap}>
<Animated.View
key={color}
entering={BounceIn}
style={{ backgroundColor: color, width: 30, height: 30 }}
/>
</GestureDetector>
);
}
function renderItem({ item }: { item: { name: string } }) {
return (
<View
style={{
width: '100%',
height: 50,
backgroundColor: 'red',
flexDirection: 'row',
justifyContent: 'space-between',
padding: 10,
alignItems: 'center',
}}>
<Text>{item.name}</Text>
<Item />
</View>
);
}
export default function Example() {
return (
<View style={{ flex: 1 }}>
<FlatList style={{ flex: 1 }} data={items} renderItem={renderItem} />
</View>
);
}
```
</details>
Code to test the second commit:
<details>
<summary>Expand</summary>
```jsx
import React from 'react';
import { View } from 'react-native';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated from 'react-native-reanimated';
function Item() {
console.log('render item');
return (
<Animated.View
style={{
alignSelf: 'center',
width: 200,
height: 200,
backgroundColor: 'red',
}}
/>
);
}
export default function Example() {
const gesture = Gesture.Tap()
.onStart(() => {
console.log('a', _WORKLET);
})
.runOnJS(true);
console.log('render parent');
return (
<View style={{ flex: 1 }}>
<GestureDetector gesture={gesture}>
<Item />
</GestureDetector>
</View>
);
}
```
Change between `View` and `Animated.View` while the app is running and
check if the tap still works. Remove `.runOnJS(true)` to test using
Reanimated.
</details>
0 commit comments