Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information