Skip to content

Commit

Permalink
Fix GestureDetector not working when its children change (#2921)
Browse files Browse the repository at this point in the history
## Description

On web `findNodeHandle` was returning the ref it received as an
argument, which in the case of GestureDetector was a reference to the
Wrap component. This worked fine until the children of the wrap changed
(but the wrap itself didn't, which caused the handlers not to be
reattached), in which case the new view didn't have event listeners
added and gestures didn't work.

This PR changes the used method to always call `findNodeHandle` which
returns the reference to the underlying DOM element, and view change
detection works correctly in this case.

## Test plan

<details>
<summary>Tested on the example app and on the following code:</summary>

```jsx
import React, { useState } from 'react';
import { Button, StyleSheet, View } from 'react-native';
import { GestureDetector, Gesture } from 'react-native-gesture-handler';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
} from 'react-native-reanimated';

function Ball(props) {
  const isPressed = useSharedValue(false);
  const offset = useSharedValue({ x: 0, y: 0 });

  const animatedStyles = useAnimatedStyle(() => {
    return {
      transform: [
        { translateX: offset.value.x },
        { translateY: offset.value.y },
        { scale: withSpring(isPressed.value ? 1.2 : 1) },
      ],
      backgroundColor: isPressed.value ? 'yellow' : 'blue',
    };
  });

  const gesture = Gesture.Pan()
    .onBegin(() => {
      'worklet';
      isPressed.value = true;
    })
    .onChange((e) => {
      'worklet';
      offset.value = {
        x: e.changeX + offset.value.x,
        y: e.changeY + offset.value.y,
      };
    })
    .onFinalize(() => {
      'worklet';
      isPressed.value = false;
    });

  return (
    <GestureDetector gesture={gesture}>
      <Animated.View style={[styles.ball, animatedStyles]} key={props.counter} />
    </GestureDetector>
  );
}

export default function Example() {
  const [counter, setCounter] = useState(0);

  return (
    <View style={styles.container}>
      <Button title="Remount" onPress={() => setCounter((prev) => prev + 1)} />
      <Ball counter={counter} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  ball: {
    width: 100,
    height: 100,
    borderRadius: 100,
    backgroundColor: 'blue',
    alignSelf: 'center',
  },
});
```
</details>
  • Loading branch information
j-piasecki authored May 22, 2024
1 parent 0a10f22 commit b87eb2b
Show file tree
Hide file tree
Showing 3 changed files with 4 additions and 9 deletions.
8 changes: 2 additions & 6 deletions src/handlers/gestures/GestureDetector/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
/* eslint-disable react/no-unused-prop-types */
import React, { useContext, useEffect, useMemo, useRef } from 'react';
import { Platform, findNodeHandle } from 'react-native';
import { GestureType } from '../gesture';
import {
findNodeHandle,
UserSelect,
TouchAction,
} from '../../gestureHandlerCommon';
import { UserSelect, TouchAction } from '../../gestureHandlerCommon';
import { ComposedGesture } from '../gestureComposition';
import { isJestEnv } from '../../../utils';
import { Platform } from 'react-native';

import GestureHandlerRootViewContext from '../../../GestureHandlerRootViewContext';
import { AttachedGestureState, GestureDetectorState } from './types';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useCallback } from 'react';
import { findNodeHandle } from 'react-native';
import { GestureType } from '../gesture';
import { findNodeHandle } from '../../gestureHandlerCommon';
import { ComposedGesture } from '../gestureComposition';

import {
Expand Down
3 changes: 1 addition & 2 deletions src/handlers/gestures/GestureDetector/useViewRefHandler.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { findNodeHandle } from '../../gestureHandlerCommon';

import { isFabric, tagMessage } from '../../../utils';
import { getShadowNodeFromRef } from '../../../getShadowNodeFromRef';

import { GestureDetectorState } from './types';
import React, { useCallback } from 'react';
import { findNodeHandle } from 'react-native';

declare const global: {
isFormsStackingContext: (node: unknown) => boolean | null; // JSI function
Expand Down

0 comments on commit b87eb2b

Please sign in to comment.