Swipe, Drag und Pinch Gesten souverän steuern
Wer Touch-Interaktionen in React Native über die alte PanResponder-API baut, verschenkt Reaktionsgeschwindigkeit und riskiert verlorene Touch-Events unter Last. Der Gesture Handler erkennt Swipe-, Drag- und Pinch-Gesten nativ, noch bevor der JavaScript-Thread überhaupt beteiligt ist, und liefert damit die Grundlage für swipefähige Produktlisten, Drag-Karten und Pinch-to-Zoom-Bildviewer in mobilen Commerce-Apps.
Inhaltsverzeichnis
- 1. Warum react-native-gesture-handler existiert
- 2. Die deklarative Gesture API im Überblick
- 3. GestureDetector: native Views an Gesten binden
- 4. Gesten komponieren: Race, Simultaneous und Exclusive
- 5. Praxisbeispiel: Swipe-to-Delete in einer Liste
- 6. Praxisbeispiel: Draggable Card mit Spring-Back
- 7. Praxisbeispiel: Pinch-to-Zoom im Bildviewer
- 8. Gestenkonflikte mit ScrollView und FlatList lösen
- 9. PanResponder vs. Gesture Handler im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum react-native-gesture-handler existiert
Die eingebaute PanResponder-API von React Native war lange Zeit der Standardweg, um Touch-Gesten zu erkennen, und sie funktioniert im Kern über eine einfache Idee: Jedes Touch-Event wird über die Bridge vom nativen Layer an den JavaScript-Thread geschickt, dort ausgewertet und die Entscheidung über Aktivierung oder Ablehnung der Geste zurückgegeben. Genau das ist auch das Problem. Sobald der JS-Thread mit Rendering, Datenverarbeitung oder Netzwerklogik beschäftigt ist, verzögert sich die Auswertung der Touch-Events, und eine Swipe-Geste fühlt sich ruckelig statt flüssig an. In einer Produktliste mit hunderten Items oder während eines Netzwerk-Requests wird dieser Effekt schnell spürbar, und genau hier setzt der Gesture Handler an.
react-native-gesture-handler verlagert die Gestenerkennung vollständig in den nativen Layer: auf iOS über UIGestureRecognizer, auf Android über ein äquivalentes natives Erkennungssystem. Touch-Events werden nicht mehr Event für Event über die Bridge geschickt, sondern direkt nativ ausgewertet, während nur das Ergebnis, etwa eine Positionsänderung oder ein Abschlussstatus, an die JavaScript-Seite gemeldet wird. Ein Gesture Handler blockiert dadurch nicht, wenn der JS-Thread gerade beschäftigt ist, und verliert auch unter Last keine Touch-Events, weil die eigentliche Erkennung nie vom Zustand des JS-Threads abhängig war.
Für mobile Commerce-Apps ist dieser Unterschied kein akademisches Detail. Swipefähige Produktgalerien, Drag-to-Reorder im Warenkorb oder ein Pinch-to-Zoom-Bildviewer für Produktfotos sind Interaktionen, bei denen Nutzer jede Verzögerung sofort als schlechte App-Qualität wahrnehmen. Ein Gesture Handler, der native Geschwindigkeit liefert, ist deshalb eine der wenigen Investitionen, die sich direkt in wahrgenommener Performance niederschlagen, unabhängig davon, wie komplex die restliche App-Logik im Hintergrund ist.
#!/usr/bin/env bash
# Install react-native-gesture-handler and the required native modules
npm install react-native-gesture-handler react-native-reanimated
# iOS: install pods for the native gesture recognizers
cd ios && pod install && cd ..
# Android: autolinking handles the native module (RN 0.60+),
# no manual MainActivity changes required in most setups
# babel.config.js needs the Reanimated plugin, always listed last:
# plugins: ['react-native-reanimated/plugin']
# Wrap the app root once in App.tsx:
# import { GestureHandlerRootView } from 'react-native-gesture-handler';
# export default function App() {
# return (
# <GestureHandlerRootView style={{ flex: 1 }}>
# <RootNavigator />
# </GestureHandlerRootView>
# );
# }
2. Die deklarative Gesture API im Überblick
Seit Version 2 bietet der Gesture Handler eine deklarative Gesture API, die die ältere, komponentenbasierte API mit PanGestureHandler und TapGestureHandler als eigenständigen Komponenten ablöst. Statt eine Geste als JSX-Komponente zu deklarieren, wird sie als Konfigurationsobjekt gebaut: Gesture.Pan(), Gesture.Pinch(), Gesture.Tap() und Gesture.LongPress() liefern jeweils einen Gesture-Builder, an den sich über Method-Chaining Optionen und Callbacks anhängen lassen. Das Ergebnis ist deutlich lesbarer Code, weil Konfiguration und Verhalten einer Geste an einer Stelle definiert werden, statt über Props einer Komponente verteilt zu sein.
Jede Geste im Gesture Handler kennt dieselben grundlegenden Callbacks: onBegin feuert, sobald die Geste erkannt wird, aber bevor sie aktiv ist, onStart markiert den Übergang in den aktiven Zustand, onUpdate liefert bei kontinuierlichen Gesten wie Pan oder Pinch fortlaufend neue Werte, und onEnd beziehungsweise onFinalize schließen die Geste ab. Bei Gesture.Pan() enthält jedes Update-Event Felder wie translationX, translationY und velocityX, bei Gesture.Pinch() einen kontinuierlichen scale-Faktor relativ zum Gesture-Start. Gesture.Tap() und Gesture.LongPress() sind diskrete Gesten, die einmalig feuern, lassen sich aber über numberOfTaps() oder minDuration() feinjustieren.
Ein entscheidender Vorteil der neuen Gesture API ist, dass sich diese Callbacks direkt mit Reanimated verbinden lassen, sofern sie mit dem Worklet-Modifier laufen. Werte wie translationX lassen sich damit ohne Umweg über den JavaScript-Thread direkt in einen Shared Value schreiben, der wiederum eine Animation antreibt. Der Gesture Handler übernimmt die Erkennung, Reanimated übernimmt die Animation, und beide laufen auf dem UI-Thread, ohne dass ein einzelnes Frame den langsameren JS-Thread durchqueren muss.
import { GestureDetector, Gesture } from 'react-native-gesture-handler';
import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';
function GestureBasicsDemo() {
const scale = useSharedValue(1);
const pressed = useSharedValue(false);
// Tap gesture: fires once per finger-down and finger-up cycle
const tap = Gesture.Tap()
.maxDuration(250)
.onStart(() => {
pressed.value = true;
})
.onEnd(() => {
pressed.value = false;
});
// Long press gesture: fires after a minimum hold duration
const longPress = Gesture.LongPress()
.minDuration(500)
.onStart(() => {
scale.value = withSpring(1.1);
});
// Pinch gesture: reports a continuous scale factor per update
const pinch = Gesture.Pinch()
.onUpdate((event) => {
scale.value = event.scale;
})
.onEnd(() => {
scale.value = withSpring(1);
});
// Race: only the gesture that activates first wins, others are cancelled
const tapOrLongPress = Gesture.Race(tap, longPress);
// Simultaneous: both branches can stay active at the same time
const composed = Gesture.Simultaneous(tapOrLongPress, pinch);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: scale.value }],
}));
return (
<GestureDetector gesture={composed}>
<Animated.View style={[styles.box, animatedStyle]} />
</GestureDetector>
);
}
3. GestureDetector: native Views an Gesten binden
GestureDetector ist die einzige Komponente, die in der neuen Gesture API benötigt wird, um eine oder mehrere Gesten an eine View zu binden. Anders als bei der alten API, in der jeder Handler-Typ eine eigene Wrapper-Komponente war, nimmt GestureDetector ein einziges gesture-Prop entgegen und rendert genau ein direktes Kind. Dieses Kind muss eine native View sein, die einen Ref akzeptiert, üblicherweise Animated.View aus Reanimated, wenn die Geste eine Animation antreiben soll, oder eine einfache View für rein logische Gesten ohne visuelles Feedback über Transform.
Ein häufiger Einsteigerfehler ist, GestureDetector um eine Komponente zu legen, die selbst kein natives View-Element rendert, etwa eine reine Funktionskomponente ohne forwardRef. Der Gesture Handler kann dann keinen nativen Handle finden, an den er die Geste binden soll, und die Geste bleibt wirkungslos. Die Lösung ist entweder, direkt auf native Primitives wie View, Image oder Animated.View zu zielen, oder eine eigene Komponente korrekt mit React.forwardRef zu versehen, damit der zugrunde liegende native Handle durchgereicht wird.
Weil GestureDetector selbst keine zusätzliche Bridging-Kommunikation für jedes Frame benötigt, bleibt der Overhead beim Verschachteln mehrerer Detektoren gering. In der Praxis lässt sich so etwa ein Kartendeck bauen, bei dem jede Karte einen eigenen GestureDetector mit eigenem Pan-Gesture trägt, während die übergeordnete Liste unabhängig scrollt, solange Aktivierungsbereiche und Prioritäten sauber über simultaneousHandlers und die Composition-APIs geregelt sind, die im nächsten Abschnitt folgen.
4. Gesten komponieren: Race, Simultaneous und Exclusive
Sobald mehr als eine Geste an derselben View oder an ineinander verschachtelten Views hängt, muss der Gesture Handler wissen, wie sich die Gesten zueinander verhalten sollen. Dafür stellt die Gesture API drei Composition-Funktionen bereit. Gesture.Race(...) lässt mehrere Gesten gegeneinander antreten: Sobald eine von ihnen aktiviert wird, werden alle anderen sofort verworfen. Das ist die richtige Wahl, wenn Tap und Long Press auf derselben Fläche liegen und sich gegenseitig ausschließen sollen.
Gesture.Simultaneous(...) erlaubt dagegen, dass mehrere Gesten parallel aktiv bleiben, etwa wenn eine Pinch-Geste zum Zoomen gleichzeitig mit einer Pan-Geste zum Verschieben des gezoomten Bildausschnitts laufen soll. Beide Gesten erhalten ihre eigenen Callbacks und aktualisieren unabhängige Shared Values, ohne sich gegenseitig zu blockieren. Gesture.Exclusive(...) schließlich definiert eine Prioritätsreihenfolge: Die zuerst übergebene Geste bekommt Vorrang, und nur wenn sie fehlschlägt oder gar nicht erst aktiviert wird, bekommt die nächste Geste in der Liste die Chance. Ein Doppel-Tap vor einer einzelnen Pinch-Geste ist ein typisches Beispiel, bei dem Exclusive verhindert, dass ein Doppel-Tap versehentlich als zwei einzelne Tap-Gesten interpretiert wird.
Diese drei Composition-Funktionen sind keine kosmetische Ergänzung, sondern der eigentliche Kern dessen, was den Gesture Handler von der alten, imperativen Handler-Verkettung unterscheidet. Statt manuell zu prüfen, welcher Handler zuerst reagiert hat, und Zustände zwischen mehreren PanResponder-Instanzen zu synchronisieren, beschreibt man mit Race, Simultaneous und Exclusive deklarativ, wie sich Gesten zueinander verhalten sollen, und überlässt die eigentliche Konfliktauflösung dem nativen Erkennungssystem.
5. Praxisbeispiel: Swipe-to-Delete in einer Liste
Swipe-to-Delete ist eines der häufigsten Gesten-Muster in mobilen Shopping-Apps, etwa um einen Artikel aus dem Warenkorb oder der Merkliste zu entfernen. Die Grundidee: Ein Gesture.Pan() erkennt eine horizontale Wischbewegung, ein Shared Value translateX folgt dem Finger, und beim Loslassen entscheidet entweder die zurückgelegte Distanz oder die Geschwindigkeit der Geste, ob das Item gelöscht oder in die Ausgangsposition zurückgeschoben wird. Wichtig ist die Option activeOffsetX, die dem Gesture Handler mitteilt, ab welcher horizontalen Bewegung die Pan-Geste überhaupt erst aktiviert werden soll, damit vertikales Scrollen in der Liste nicht versehentlich als Swipe interpretiert wird.
Für den eigentlichen Löschvorgang wird runOnJS benötigt, weil der Callback in onEnd als Worklet auf dem UI-Thread läuft, während das Entfernen des Items aus dem State zwingend auf dem JavaScript-Thread passieren muss. Diese Trennung ist typisch für den Gesture Handler in Kombination mit Reanimated: Die Geste selbst, ihre kontinuierliche Aktualisierung und selbst die Abschlussanimation laufen komplett nativ, nur der eigentliche Datenzustand wird am Ende einmalig über die Bridge zurückgemeldet.
import React from 'react';
import { StyleSheet } from 'react-native';
import { GestureDetector, Gesture } from 'react-native-gesture-handler';
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
runOnJS,
} from 'react-native-reanimated';
const DELETE_THRESHOLD = -120;
function SwipeableListItem({ item, onDelete }) {
const translateX = useSharedValue(0);
const pan = Gesture.Pan()
.activeOffsetX([-10, 10]) // ignore near-vertical drags, let the list scroll instead
.onUpdate((event) => {
// Only allow swiping to the left
translateX.value = Math.min(0, event.translationX);
})
.onEnd((event) => {
const shouldDelete = translateX.value < DELETE_THRESHOLD || event.velocityX < -800;
if (shouldDelete) {
translateX.value = withTiming(-500, { duration: 200 }, () => {
runOnJS(onDelete)(item.id);
});
} else {
translateX.value = withTiming(0);
}
});
const rowStyle = useAnimatedStyle(() => ({
transform: [{ translateX: translateX.value }],
}));
return (
<GestureDetector gesture={pan}>
<Animated.View style={[styles.row, rowStyle]}>
<Animated.Text style={styles.title}>{item.title}</Animated.Text>
</Animated.View>
</GestureDetector>
);
}
6. Praxisbeispiel: Draggable Card mit Spring-Back
Eine frei verschiebbare Karte, die nach dem Loslassen mit einer Feder-Animation zurückschnellt, ist ein zweites klassisches Gesture-Handler-Muster, etwa für Drag-to-Reorder-Listen im Warenkorb oder für spielerische Onboarding-Interaktionen. Die Herausforderung dabei ist, dass eine Pan-Geste per Definition immer relative Bewegungswerte liefert, die bei jeder neuen Geste wieder bei null beginnen. Ohne zusätzliche Zustandshaltung würde die Karte bei jedem erneuten Greifen auf die ursprüngliche Position zurückspringen, statt von der aktuellen Position aus weiterzumachen.
Die Lösung sind zwei zusätzliche Shared Values, startX und startY, die im onStart-Callback den aktuellen Stand von translateX und translateY zwischenspeichern. Jede folgende onUpdate-Aktualisierung addiert die relative Bewegung der Geste auf diesen gespeicherten Startwert, statt ihn zu überschreiben. Beim Loslassen übernimmt withSpring aus Reanimated die Rückkehr zur Ausgangsposition mit einer natürlich wirkenden Federkurve, deren damping- und stiffness-Parameter direkt steuern, wie schnell und wie stark die Karte einschwingt.
import React from 'react';
import { GestureDetector, Gesture } from 'react-native-gesture-handler';
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
} from 'react-native-reanimated';
function DraggableProductCard() {
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
const startX = useSharedValue(0);
const startY = useSharedValue(0);
const pan = Gesture.Pan()
.onStart(() => {
// Remember the current offset, avoids a jump when re-grabbing the card
startX.value = translateX.value;
startY.value = translateY.value;
})
.onUpdate((event) => {
translateX.value = startX.value + event.translationX;
translateY.value = startY.value + event.translationY;
})
.onEnd(() => {
// Snap back to the origin with a natural spring curve
translateX.value = withSpring(0, { damping: 14, stiffness: 140 });
translateY.value = withSpring(0, { damping: 14, stiffness: 140 });
});
const cardStyle = useAnimatedStyle(() => ({
transform: [
{ translateX: translateX.value },
{ translateY: translateY.value },
],
}));
return (
<GestureDetector gesture={pan}>
<Animated.View style={[styles.card, cardStyle]} />
</GestureDetector>
);
}
7. Praxisbeispiel: Pinch-to-Zoom im Bildviewer
Für Produktbilder in einem Onlineshop ist Pinch-to-Zoom eine der am meisten erwarteten Gesten überhaupt, weil Nutzer aus nativen Foto-Apps genau dieses Verhalten kennen. Gesture.Pinch() liefert dafür in jedem onUpdate-Callback einen relativen scale-Faktor, der sich auf den Zustand beim Start der Geste bezieht, nicht auf einen absoluten Zoomlevel. Deshalb braucht auch diese Geste einen zusätzlichen Shared Value, savedScale, der den zuletzt erreichten Zoomfaktor über mehrere Pinch-Gesten hinweg festhält, damit ein zweites Pinchen nahtlos am vorherigen Zoomstand ansetzt statt wieder bei eins zu beginnen.
Ein produktionsreifer Pinch-to-Zoom-Handler braucht außerdem Grenzen: Ohne Begrenzung ließe sich ein Bild beliebig klein schrumpfen oder beliebig weit aufziehen, bis Texturqualität und Layout darunter leiden. Math.min und Math.max um den berechneten Scale-Wert stellen sicher, dass der Zoom innerhalb sinnvoller Grenzen bleibt, während ein zusätzliches Gesture.Tap() mit numberOfTaps(2) als Doppel-Tap-Geste den Zoom zurücksetzt. Über Gesture.Exclusive() bekommt der Doppel-Tap Vorrang vor der Pinch-Geste, damit ein schnelles Doppeltippen nicht gleichzeitig als Beginn einer Pinch-Bewegung fehlinterpretiert wird.
import React from 'react';
import { GestureDetector, Gesture } from 'react-native-gesture-handler';
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
} from 'react-native-reanimated';
function PinchZoomImage({ source }) {
const scale = useSharedValue(1);
const savedScale = useSharedValue(1);
const pinch = Gesture.Pinch()
.onUpdate((event) => {
// Clamp so the image cannot shrink below original size or zoom too far
scale.value = Math.min(Math.max(savedScale.value * event.scale, 1), 4);
})
.onEnd(() => {
savedScale.value = scale.value;
if (scale.value < 1.05) {
scale.value = withTiming(1);
savedScale.value = 1;
}
});
// Double tap resets the zoom, a common companion gesture for image viewers
const doubleTap = Gesture.Tap()
.numberOfTaps(2)
.onStart(() => {
scale.value = withTiming(1);
savedScale.value = 1;
});
// Exclusive: the double tap gets priority over the pinch gesture
const composed = Gesture.Exclusive(doubleTap, pinch);
const imageStyle = useAnimatedStyle(() => ({
transform: [{ scale: scale.value }],
}));
return (
<GestureDetector gesture={composed}>
<Animated.Image source={source} style={[styles.image, imageStyle]} />
</GestureDetector>
);
}
8. Gestenkonflikte mit ScrollView und FlatList lösen
Sobald ein Gesture Handler innerhalb einer scrollbaren Liste eingesetzt wird, entsteht zwangsläufig eine Konkurrenzsituation: Ein horizontaler Swipe soll das Listenitem bewegen, während ein vertikaler Swipe die Liste selbst scrollen soll, und beide Bewegungen starten mit einer sehr ähnlichen initialen Touch-Geste. ScrollView und FlatList bringen intern bereits einen eigenen nativen Pan-Handler für das Scrollen mit, und ohne explizite Konfiguration konkurriert dieser interne Handler direkt mit jedem selbst definierten Gesture Handler in den Listenitems.
Die erste Verteidigungslinie ist, die Aktivierungsschwelle jeder eigenen Pan-Geste über activeOffsetX und failOffsetY präzise einzugrenzen, wie es im Swipe-to-Delete-Beispiel bereits gezeigt wurde. Eine Pan-Geste, die nur bei überwiegend horizontaler Bewegung aktiviert und bei überwiegend vertikaler Bewegung sofort fehlschlägt, überlässt der Liste jede Bewegung, die eindeutig als Scroll-Absicht erkennbar ist. Reicht das nicht aus, weil zum Beispiel zwei unabhängige Gesture Handler dieselbe Touch-Sequenz gleichzeitig auswerten sollen, kommt simultaneousHandlers ins Spiel: Damit lässt sich einem Gesture Handler explizit mitteilen, mit welchem anderen Handler, etwa dem internen Scroll-Handler einer ScrollView-Referenz, er gleichzeitig aktiv sein darf, statt sich gegenseitig zu blockieren.
In der Praxis bewährt sich die Kombination aus beiden Techniken: Aktivierungsschwellen als erste, günstige Filterstufe direkt im nativen Erkennungssystem, und simultaneousHandlers beziehungsweise die Composition-APIs aus Abschnitt vier als gezielte Ausnahme für die Fälle, in denen zwei Gesten tatsächlich gleichzeitig ausgewertet werden sollen. Wer diese Reihenfolge einhält, vermeidet die häufigste Ursache für ruckelige oder komplett blockierte Listen in Produktions-Apps mit vielen verschachtelten Gesture Handlern.
9. PanResponder vs. Gesture Handler im Vergleich
Die Entscheidung zwischen der alten PanResponder-API und dem Gesture Handler ist heute in den meisten neuen React-Native-Projekten bereits gefallen, aber der direkte Vergleich zeigt, warum. Die folgende Tabelle stellt die wichtigsten Dimensionen gegenüber, entlang derer sich beide Ansätze in der Praxis unterscheiden.
| Dimension | PanResponder | react-native-gesture-handler | Vorteil |
|---|---|---|---|
| Erkennungs-Thread | JavaScript-Thread über die Bridge | Nativer UI-Thread | Keine Bridge-Latenz pro Touch-Event |
| Reaktion unter Last | Verlorene oder verzögerte Events bei JS-Blockade | Bleibt flüssig, unabhängig vom JS-Thread | Stabile Reaktionsgeschwindigkeit |
| Gesten kombinieren | Manuelle Zustandslogik pro Handler-Instanz | Gesture.Race/Simultaneous/Exclusive |
Deklarative, lesbare Komposition |
| Konflikt mit ScrollView | Häufig blockierend, schwer zu debuggen | activeOffsetX + simultaneousHandlers |
Gezielte, konfigurierbare Priorität |
| Lernkurve | Vertraut, aber viel Boilerplate | Neue API, aber deutlich weniger Code | Schnellere Einarbeitung mit Reanimated |
In der Summe zeigt der Vergleich, dass der Gesture Handler nicht nur eine schnellere, sondern auch eine strukturell andere Lösung ist. Während PanResponder Gestenerkennung als Nebenprodukt des JS-Threads behandelt, macht der Gesture Handler die native Touch-Erkennung zum eigentlichen Fundament, und die deklarative Gesture API sorgt zusätzlich dafür, dass komplexe Gesten-Kompositionen nicht mehr in verschachtelter, schwer wartbarer Zustandslogik enden.
Mironsoft
Touch-freundliche React-Native-Apps für Magento-basierten Mobile Commerce
Gesten, die sich nativ anfühlen, statt nur so aussehen?
Wir bauen React-Native-Storefronts für Magento-Shops mit swipefähigen Produktgalerien, Drag-to-Reorder im Warenkorb und Pinch-to-Zoom-Bildviewern, umgesetzt mit react-native-gesture-handler und Reanimated für spürbar flüssige Interaktionen.
UX-Beratung
Analyse bestehender Touch-Interaktionen und Priorisierung der Gesten mit dem größten UX-Hebel
Gesture-Implementierung
Swipe-to-Delete, Drag-Karten und Pinch-to-Zoom mit react-native-gesture-handler und Reanimated
Performance-Tuning
Gestenkonflikte mit Listen und ScrollViews auflösen, Anbindung an Magento-Warenkorb und Katalog
10. Zusammenfassung
Der Gesture Handler löst ein sehr konkretes Problem der alten PanResponder-API: Touch-Erkennung, die vom JavaScript-Thread abhängt, wird unter Last unzuverlässig und fühlt sich nie so flüssig an wie native Interaktionen. Indem react-native-gesture-handler die Erkennung von Swipe-, Drag- und Pinch-Gesten vollständig in den nativen Layer verlagert, bleibt die Reaktionsgeschwindigkeit auch dann stabil, wenn der JS-Thread mit anderer Arbeit beschäftigt ist. Die deklarative Gesture API mit Gesture.Pan(), Gesture.Pinch(), Gesture.Tap() und Gesture.LongPress() macht einzelne Gesten übersichtlich konfigurierbar, während Gesture.Race, Gesture.Simultaneous und Gesture.Exclusive auch komplexe Kombinationen mehrerer Gesten beherrschbar halten.
In der Praxis zeigen sich die Stärken des Gesture Handlers besonders deutlich an konkreten Mustern wie Swipe-to-Delete, einer frei verschiebbaren Karte mit Spring-Back oder einem Pinch-to-Zoom-Bildviewer, die alle drei auf demselben Grundprinzip beruhen: GestureDetector bindet eine Geste an eine native View, Shared Values aus Reanimated speichern den Zustand zwischen den Frames, und nur der finale Datenzustand wird bei Bedarf über runOnJS an den JavaScript-Thread zurückgemeldet. Wer zusätzlich Aktivierungsschwellen wie activeOffsetX und Werkzeuge wie simultaneousHandlers gezielt einsetzt, vermeidet die häufigsten Konflikte mit ScrollView und FlatList und liefert Gesten-Interaktionen, die sich in mobilen Commerce-Apps nativ anfühlen.
Gesture Handler in React Native: Das Wichtigste auf einen Blick
Nativer statt JS-Thread
Der Gesture Handler erkennt Gesten nativ über UIGestureRecognizer und Android-Äquivalente, unabhängig von der Auslastung des JS-Threads.
Deklarative Gesture API
Gesture.Pan(), Gesture.Pinch(), Gesture.Tap() und Gesture.LongPress() ersetzen die alten Handler-Komponenten durch Method-Chaining.
Komposition per Race/Simultaneous/Exclusive
Mehrere Gesten lassen sich deklarativ kombinieren, statt manuelle Zustandslogik zwischen Handlern zu synchronisieren.
Konflikte gezielt auflösen
activeOffsetX und simultaneousHandlers verhindern Blockaden zwischen eigenen Gesten und ScrollView/FlatList.