Upgrading to the new API introduced in Gesture Handler 2
Make sure to migrate off the RNGestureHandlerEnabledRootView
(Android only)
Gesture Handler 1 required you to override createRootView
to return an instance of RNGestureHandlerEnabledRootView
. This class has been the cause of many hard to debug and fix crashed and was deprecated in version 2.0, and subsequently removed in version 2.4. If you are still using it, check out migrating off RNGHEnabledRootView guide.
Upgrading to the new API
The most important change brought by the Gesture Handler 2 is the new Gesture API, along with the GestureDetector
component. It makes declaring gesture easier, as it handles much of the work under the hood and reduces the amount of necessary boilerplate code. Instead of a separate component for every type of gesture, the GestureDetector
component is used to attach gestures to the underlying view based on the configuration object passed to it. The configuration objects are created using the Gesture
object, here is a simple example:
const tapGesture = Gesture.Tap().onStart(() => {
console.log('Tap!');
});
...
return (
<GestureDetector gesture={tapGesture}>
<View />
</GestureDetector>
);
As you can see, there are no onGestureEvent
and onHandlerStateChange
callbacks, instead the state machine is handled under the hood and relevant callbacks are called for specific transitions or events:
onBegin
- called when the gesture transitions to theBEGAN
state, which in most cases is when the gesture starts processing the touch stream - when the finger first touches the viewonStart
- called when the activation criteria for the gesture are met and it transitions fromBEGAN
toACTIVE
stateonUpdate
- replacesonGestureEvent
, called every time the gesture sends a new event while it's in theACTIVE
stateonChange
- if defined, called just afteronUpdate
, the events passed to it are the same as the ones passed toonUpdate
but they also containchange
values which hold the change in value they represent since the last event (i.e. in case of thePan
gesture, the event will also containchangeX
andchangeY
properties)onEnd
- called when the gesture transitions from theACTIVE
state to either ofEND
,FAILED
orCANCELLED
- you can tell whether the gesture finished due to user interaction or because of other reason (like getting cancelled by the system, or failure criteria) using the second value passed to theonEnd
callback alongside the eventonFinalize
called when the gesture transitions into either ofEND
,FAILED
orCANCELLED
state, if the gesture wasACTIVE
,onEnd
will be called first (similarly toonEnd
you can determine the reason for finishing using the second argument)
The difference between onEnd
and onFinalize
is that the onEnd
will be called only if the gesture was ACTIVE
, while onFinalize
will be called if the gesture has BEGAN
. This means that you can use onEnd
to clean up after onStart
, and onFinalize
to clean up after onBegin
(or both onBegin
and onStart
).
Configuring the gestures
The new gesture objects are configured in the builder-like pattern. Instead of properties, each gesture provides methods that allow for its customization. In most cases the names of the methods are the same as the relevant props, ot at least very similar. For example:
return (
<TapGestureHandler
numberOfTaps={2}
maxDurationMs={500}
maxDelayMs={500}
maxDist={10}
onHandlerStateChange={({ nativeEvent }) => {
if (nativeEvent.state === State.ACTIVE) {
console.log('Tap!');
}
}}>
<View />
</TapGestureHandler>
);
Would have the same effect as:
const tapGesture = Gesture.Tap()
.numberOfTaps(2)
.maxDuration(500)
.maxDelay(500)
.maxDistance(10)
.onStart(() => {
console.log('Tap!');
});
return (
<GestureDetector gesture={tapGesture}>
<View />
</GestureDetector>
);
You can check the modifiers available to specific gestures in the API Reference under Gestures.
Using multiple gestures on the same view
Using the gesture handler components, if you wanted to have multiple gestures on one view, you would have to stack them on top of each other and, in case you wanted to use animations, add an Animated.View
after each handler, resulting in a deep component tree, for example:
return (
<TapGestureHandler ... >
<Animated.View>
<PanGestureHandler ... >
<Animated.View>
<PinchGestureHandler ... >
<YourView />
</PinchGestureHandler>
</Animated.View>
</PanGestureHandler>
</Animated.View>
</TapGestureHandler>
);
With the GestureDetector
you can use the Gesture Composition API to stack the gestures onto one view:
const tapGesture = Gesture.Tap();
const panGesture = Gesture.Pan();
const pinchGesture = Gesture.Pinch();
return (
<GestureDetector gesture={Gesture.Race(tapGesture, panGesture, pinchGesture)}>
<YourView />
</GestureDetector>
);
Similarly, you can use Gesture.Simultaneous
to replace stacked gesture handlers that should be able to recognize gestures simultaneously, and Gesture.Exclusive
to replace stacked gesture handlers that require failure of others.
Replacing waitFor
and simultaneousHandlers
If you want to make relations between the gestures attached to the same view, you should use the Gesture Composition API described above. However, if you want to make a relation between gestures attached to different views, or between gesture and an old gesture handler, you should use simultaneousWithExternalGesture
instead of simultaneousHandlers
, and requireExternalGestureToFail
instead of waitFor
. In case you need a ref object to pass to an old gesture handler, you can set it to the gesture using .withRef(refObject)
modifier.