Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix GestureDetector not working when its children change #2921

Merged
merged 3 commits into from
May 22, 2024

Conversation

j-piasecki
Copy link
Member

@j-piasecki j-piasecki commented May 21, 2024

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

Tested on the example app and on the following code:
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',
  },
});

@j-piasecki j-piasecki requested a review from m-bert May 21, 2024 13:02
Copy link
Contributor

@m-bert m-bert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not so happy about using findNodeHandle - afaik it uses findDOMNode under the hood, which is marked as deprecated and it is meant to be removed in future major version of React. We already started removing it on web in Reanimated.

However, I think we can merge this now and later on we can think of how we can deal with this problem, if returning node is not enough.

Also, shouldn't we replace all occurrences of findNodeHandle? Right now we still have places where we use the one from gestureHandlerCommon, is it necessary?

@j-piasecki
Copy link
Member Author

I agree about removing it, though it's fixing the problem and we're already using it anyway (in the delegate).

Also, shouldn't we replace all occurrences of findNodeHandle? Right now we still have places where we use the one from gestureHandlerCommon, is it necessary?

I think all references in the code related to the new API is updated. I'd rather not touch the old API as to not break anything accidentally, especially at the point where I don't know why the custom findNodeHandle was working like that in the first place.

@m-bert
Copy link
Contributor

m-bert commented May 21, 2024

I think all references in the code related to the new API is updated.

I think this one was not updated.

I don't know why the custom findNodeHandle was working like that in the first place.

Well, I'm not sure why it was implemented this way, but I do agree that on web findNodeHandle acts almost like an identity function (though I've seen some cases where it doesn't, as far as I remember the problem was with FlatList, but I'm not sure).

@j-piasecki
Copy link
Member Author

I think this one was not updated.

Fixed

Copy link
Contributor

@m-bert m-bert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@j-piasecki j-piasecki merged commit b87eb2b into main May 22, 2024
1 check passed
@j-piasecki j-piasecki deleted the @jpiasecki/fix-detector-remount-web branch May 22, 2024 10:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants