In React Native Gesture Handler 3, when do you use useSimultaneousGestures, useExclusiveGestures and useCompetingGestures to combine gestures on one view?
answer
- all at once, priority, or race
- pinch, pan and rotate together
- double tap before single tap
- first to activate cancels the rest
- same view only; relations otherwise
basics
~20 suseSimultaneousGestures lets all gestures be active together, useExclusiveGestures gives them a priority order where each waits for the ones before it to fail, and useCompetingGestures lets the first to activate cancel the rest. All three combine gestures on one detector.
solid answer
~50 sA `GestureDetector` takes one gesture, so several gestures on the same view are combined with a composition hook. `useSimultaneousGestures(pinch, pan, rotation)` lets all of them be active at once, which is what a photo you can zoom, move and rotate needs. `useExclusiveGestures(doubleTap, singleTap)` sets a priority by argument order: a gesture may activate only after every earlier one has failed, so the single tap waits for the double tap to fail, which costs up to the tap's `maxDelay` of 500 ms by default. `useCompetingGestures(pan, longPress)` is a race: the first to activate cancels the others, so a photo either drags or opens a long-press menu, never both. Composition hooks accept composed gestures too, so they nest. When the gestures live on different components, such as a photo and its pager, you use the relation props `simultaneousWith`, `requireToFail` and `block` instead.
code
tsx · 48 linesimport type {ReactNode} from 'react';
import {
GestureDetector,
useExclusiveGestures,
usePinchGesture,
usePanGesture,
useSimultaneousGestures,
useTapGesture,
} from 'react-native-gesture-handler';
import Animated, {useSharedValue, withTiming} from 'react-native-reanimated';
export function PhotoGestures({children}: {children: ReactNode}) {
const scale = useSharedValue(1);
const chrome = useSharedValue(1);
const pinch = usePinchGesture({
onUpdate: e => {
scale.value = e.scale;
},
});
const pan = usePanGesture({});
const zoomAndMove = useSimultaneousGestures(pinch, pan);
const doubleTap = useTapGesture({
numberOfTaps: 2,
onDeactivate: e => {
if (!e.canceled) {
scale.value = withTiming(scale.value > 1 ? 1 : 2);
}
},
});
const singleTap = useTapGesture({
onDeactivate: e => {
if (!e.canceled) {
chrome.value = chrome.value === 1 ? 0 : 1;
}
},
});
const taps = useExclusiveGestures(doubleTap, singleTap);
const all = useSimultaneousGestures(zoomAndMove, taps);
return (
<GestureDetector gesture={all}>
<Animated.View style={{flex: 1}}>{children}</Animated.View>
</GestureDetector>
);
}go deeper
Recall the three hooks and their one-line rules: together, by priority, or first one wins.
Explain the priority rule of useExclusiveGestures, why argument order matters, and the maxDelay latency it adds to a single tap.
Choose compositions for a real screen, nest them, and know when the problem is cross-component and needs relation props instead.
Weigh gesture richness against latency and discoverability, for example whether a double tap is worth delaying every single tap.
## Why composition exists A `GestureDetector` receives exactly one `gesture` prop. When a single view needs several gestures, say a photo that can be pinched, panned and double-tapped, those gestures must be **combined into one composed gesture** that states how they interact. Gesture Handler 3 provides three composition hooks, replacing version 2's `Gesture.Simultaneous`, `Gesture.Exclusive` and `Gesture.Race`. ## The three hooks | Hook | Rule | Photo viewer use | |---|---|---| | `useSimultaneousGestures(a, b, ...)` | all may be active at the same time; one activating cancels none | pinch, pan and rotation on the photo | | `useExclusiveGestures(a, b, ...)` | only one activates; priority by argument order, and each waits for all earlier ones to fail | double tap to zoom before single tap to toggle the toolbar | | `useCompetingGestures(a, b, ...)` | only one activates; the first to activate cancels the rest | drag the photo or long-press for a share menu | | Version 2 equivalents | `Gesture.Simultaneous`, `Gesture.Exclusive`, `Gesture.Race` | legacy builder API | ## Simultaneous: gestures that belong together A zoomable photo feels natural only if two fingers can zoom, move and rotate at once. Without composition, gestures under the same touch compete, and the first to activate would cancel the others. `useSimultaneousGestures` states that activation of one does not cancel the rest, so each keeps receiving updates. ## Exclusive: priority, and its latency cost `useExclusiveGestures(doubleTap, singleTap)` means the single tap may activate only once the double tap has **failed**. That is exactly right for "double tap zooms, single tap toggles the toolbar", but it has a cost: - After the first tap, the double tap keeps waiting for a second tap until the tap gesture's **`maxDelay`** passes, which defaults to **500 ms**. - Only then does the double tap fail and the single tap activate. So the toolbar toggles up to about half a second late. Shortening `maxDelay` trades responsiveness against how quickly users must double-tap. Getting the order wrong, `useExclusiveGestures(singleTap, doubleTap)`, means the single tap always wins and the double tap never fires. ## Competing: a race `useCompetingGestures(pan, longPress)` lets whichever gesture activates **first** cancel the others. A quick drag moves the photo and cancels the long press; holding still until the long press's duration elapses opens the menu and cancels the pan. Neither waits for the other to fail, so there is no extra latency, only a winner. ## Nesting and scope - Composition hooks accept **composed gestures** as arguments, so you can build a tree: compose pinch and pan simultaneously, compose the taps exclusively, then combine the two results, as the code example does. - They apply to gestures attached **to the same detector**. Gestures on different components, such as a photo and the pager around it, are related with **`simultaneousWith`**, **`requireToFail`** and **`block`** in the gesture config. - Relations cannot be mixed between hook-API gestures and version 2 builder gestures. ## Choosing quickly 1. Should both be able to run together? Simultaneous. 2. Is one strictly more important, and can the other afford to wait? Exclusive, most important first. 3. Should whoever is recognised first simply win? Competing. ## Common mistakes - **Reversing the exclusive order,** so the less specific gesture (single tap) is listed first and the more specific one (double tap) can never activate. - **Using exclusive where competing is meant,** adding needless latency: a drag does not need to wait for a long press to fail if the first one recognised should win. - **Forgetting simultaneous on multi-finger gestures,** so a pinch cancels the pan and the photo stops following the fingers mid-zoom. - **Reaching for a composition hook across components,** such as a photo and its pager, where relation props are the right tool. - **Reusing one composed gesture in two detectors,** which the documentation calls undefined behaviour. - **Treating the single tap's delay as a bug** rather than the documented cost of an exclusive double tap.
- In Gesture Handler 3, why does a single tap feel slow once it is combined with a double tap in useExclusiveGestures?The single tap may activate only after the double tap fails, and the double tap keeps waiting for a second tap until its `maxDelay` passes, 500 ms by default. The single tap therefore fires up to about half a second after the finger lifts. Lower `maxDelay` to shorten the wait, or drop the double tap if the single tap must feel instant.
- What happens if two Gesture Handler gestures on the same view are not composed at all?A detector accepts only one gesture, so you cannot attach two separately to the same view without a composition hook. Gestures on different, nested detectors are not composed, and by default they compete: the first to activate cancels the others that are not declared simultaneous with it, and relation props change that.
saying these in an interview costs you the question
- useExclusiveGestures runs the gestures one after another in sequence
- The order of arguments to useExclusiveGestures does not matter
- useCompetingGestures waits for all gestures to fail before picking one
- Composition hooks are how you relate a photo's pinch to the pager around it
- A double tap exclusive with a single tap adds no delay to the single tap