Closed computerjazz closed 11 months ago
Hi @computerjazz! We've looked into your code and it seems that it uses Gesture Handler in a wrong way.
First of all, if you try to run this code on web, you'll get Handler with tag x already exists
error. Native platforms don't check that, but this error message already tells us that something is wrong.
From what we can see, you're using a recursive component that receives gestures from parent as a prop, then you combine them in Gesture.Simultaneous()
. The thing is, GestureDetector
in child tries to attach handler that already has been used in parent, thus we get error on web. On native part this gesture will be attached, but when child disappears, gestures get cleared - that's why parent becomes unresponsive. This is not a bug though - it is expected behavior.
You can try to use simultaneousWithExternalGesture instead, maybe this will help.
Thanks for the quick response @m-bert! I didn't know that a gesture can't/shouldn't be used across multiple GestureDetector
s. I tested simultaneousWithExternalGesture
and it did fix the issue in my toy example — I'll try plugging it into https://github.com/computerjazz/react-native-infinite-pager, which is where I identified the main issue.
I noticed ComposedGesture
is not a valid argument to simultaneousWithExternalGesture
— any idea if this is just a type error or if composed gestures cannot be used in this way?
I'm not sure why you used SimultaneousGesture[]
as a type in this prop. I can see that it looks pretty natural given the name of this property, but as far as I can see, you simply pass there array of gestures.
Correct me if I'm wrong, but in parent simultaneousGestures
is simply []
, and in child it is [pan]
. In that case, that is not the array of simultaneous gestures when it comes to types.
Edit: I've missed that SimultaneousGesture
is defined at the top. In that case it should be sufficient to change simultaneousGestures?: SimultaneousGesture[]
to simultaneousGestures?: GestureType[]
In my toy example that would work, but I want to be able to support simultaneous composed gestures in react-native-infinite-pager.
example where I want to wrap the DraggableSquare
in a tap gesture that has a composed gesture: https://snack.expo.dev/@easydan/removing-nested-handler-bugfix?platform=ios
It seems like the code works as expected when I use a composed gesture as a simultaneousWithExternalGesture
, despite the type error.
I've just tested that on a small example. Even though it looks like it works in your case, we can't guarantee that it will always be the case. I've prepared small repro to show what I mean:
Code above has 2 boxes, outer one has 2 Pan
gestures which can work simultaneously. Inner box has one Pan
gesture, which can work simultaneously with outer pans.
If you run code above you can see, that panning inside inner box will give logs with only one handler tag - that's why outer ones are not triggered. However, if you change this line:
.simultaneousWithExternalGesture(outerPan);
to this:
.simultaneousWithExternalGesture(...outerPan.toGestureArray());
you can see all handlers tag being logged (given that they all meet activation criteria).
Hi! I hope everything is clear now.
Given that it was expected behavior, not a problem, I will close this issue. Feel free to re-open it, or submit a new one, if you find something that needs to be addressed.
Thanks for all your help @m-bert ! Yes, all clear now, although it might be useful to more clearly document the different ways multiple gestures can be defined (and warn against doing what I did 😅). Maybe also a console warning with info on how to fix if multiple gesture detectors try to register the same gesture?
Thanks for sharing your thoughts!
I've mentioned it, but I don't know whether you've seen it yourself or not. This is how original code works on web:
I will check why native platforms do not perform this check and, if possible, I'll add either error or warning. I will also mention it within our documentation.
Description
If gesture handlers are nested with
simultaneousHandlers
enabled, removing the inner handler causes the outer handler to become unresponsive. This is true whether the inner handler is removed while a gesture is in progress or not.There is a workaround — including
myGesture.initialize()
in auseEffect
fixes it, though I'm not sure why.In the video below I'm removing the child 1 second after beginning the gesture. In the "broken" case, the gesture immediately becomes unresponsive when the child is removed, and subsequent drags also do not work. In the "fixed" case with the workaround above, everything works as expected:
Steps to reproduce
simultaneousHandlers
with parent handlersSnack or a link to a repository
https://snack.expo.dev/@easydan/removing-nested-handler-bug
Gesture Handler version
2.13.4
React Native version
0.72.3
Platforms
iOS
JavaScript runtime
Hermes
Workflow
Expo managed workflow
Architecture
Paper (Old Architecture)
Build type
Release mode
Device
Real device
Device model
iPhone 15 Pro
Acknowledgements
Yes