Skip to content

Commit 4733795

Browse files
authored
[Docs] Document gesture cancellation when a detector's view is reordered (#4623)
## Description Adds a "Views reordered during a gesture" use case to the Gesture Detectors page and a matching subsection to the upgrade guide, documenting that reordering a `GestureDetector`'s view cancels the active gesture and that `VirtualGestureDetector` keeps it alive on Android. Related: #4608 ## Test plan Read docs
1 parent b886eef commit 4733795

2 files changed

Lines changed: 117 additions & 2 deletions

File tree

‎packages/docs-gesture-handler/docs/core-components/gesture-detector.mdx‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,82 @@ const styles = StyleSheet.create({
177177
`}/>
178178

179179

180+
#### Views reordered during a gesture
181+
182+
When a re-render changes `GestureDetector`'s place in the view hierarchy (e.g., a drag-to-reorder list puts the held row at a new index), the renderer may detach the view and reattach it, which cancels the active gesture.
183+
184+
On Android, you can keep the gesture alive by wrapping the list in `InterceptingGestureDetector` and using `VirtualGestureDetector` for each row. The gesture is then attached through the list's detector, which stays in place.
185+
186+
On iOS and web the system cancels the gesture of a reattached view regardless of the detector used.
187+
188+
<CollapsibleCode
189+
label="Show full example"
190+
expandedLabel="Hide full example"
191+
lineBounds={[11, 43]}
192+
src={`
193+
import React, { useState } from 'react';
194+
import { FlatList, StyleSheet, Text, View } from 'react-native';
195+
import {
196+
GestureHandlerRootView,
197+
InterceptingGestureDetector,
198+
usePanGesture,
199+
VirtualGestureDetector,
200+
} from 'react-native-gesture-handler';
201+
202+
type RowProps = { id: string; onGrab: (id: string) => void };
203+
204+
function Row({ id, onGrab }: RowProps) {
205+
const pan = usePanGesture({
206+
runOnJS: true,
207+
onActivate: () => onGrab(id),
208+
});
209+
210+
return (
211+
<VirtualGestureDetector gesture={pan}>
212+
<View style={styles.row}>
213+
<Text style={styles.text}>{id}</Text>
214+
</View>
215+
</VirtualGestureDetector>
216+
);
217+
}
218+
219+
export default function App() {
220+
const [rows, setRows] = useState(['A', 'B', 'C', 'D']);
221+
222+
const onGrab = (id: string) =>
223+
setRows((current) => [id, ...current.filter((row) => row !== id)]);
224+
225+
return (
226+
<GestureHandlerRootView style={styles.container}>
227+
<InterceptingGestureDetector>
228+
<FlatList
229+
data={rows}
230+
keyExtractor={(id) => id}
231+
renderItem={({ item }) => <Row id={item} onGrab={onGrab} />}
232+
/>
233+
</InterceptingGestureDetector>
234+
</GestureHandlerRootView>
235+
);
236+
}
237+
238+
const styles = StyleSheet.create({
239+
container: { flex: 1, padding: 16 },
240+
row: {
241+
height: 80,
242+
marginBottom: 12,
243+
justifyContent: 'center',
244+
alignItems: 'center',
245+
backgroundColor: '#001A72',
246+
},
247+
text: {
248+
color: 'white',
249+
fontWeight: 'bold',
250+
fontSize: 20,
251+
},
252+
});
253+
`}/>
254+
255+
180256
## Properties
181257

182258
### gesture

‎packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx‎

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,9 @@ Full changes are as follows:
271271
| `blocksHandlers` | `block` |
272272

273273

274-
## Migrating components relying on view hierarchy
274+
## Migrating to virtual detectors
275275

276-
Certain components, such as `SVG`, depend on the view hierarchy to function correctly. In Gesture Handler 3, `GestureDetector` disrupts these hierarchies. To resolve this issue, two new detectors have been introduced: [`InterceptingGestureDetector`](/docs/core-components/gesture-detectors#interceptinggesturedetector) and [`VirtualGestureDetector`](/docs/core-components/gesture-detectors#virtualgesturedetector).
276+
In Gesture Handler 3, `GestureDetector` is a host view. This has two consequences: it disrupts components that depend on the view hierarchy, such as `SVG`, and it is detached and reattached when its place in the hierarchy changes, which cancels an active gesture. To handle both cases, two new detectors have been introduced: [`InterceptingGestureDetector`](/docs/core-components/gesture-detectors#interceptinggesturedetector) and [`VirtualGestureDetector`](/docs/core-components/gesture-detectors#virtualgesturedetector).
277277

278278
:::danger Detectors order
279279
`VirtualGestureDetector` has to be a descendant of `InterceptingGestureDetector`.
@@ -308,6 +308,45 @@ code2={
308308
`}
309309
/>
310310

311+
### Migrating views reordered during a gesture
312+
313+
In Gesture Handler 2, a gesture on Android survived its view being reordered mid-gesture, for example a drag-to-reorder list putting the held row at a new index. To keep that behavior, use `InterceptingGestureDetector` and `VirtualGestureDetector`. See [Views reordered during a gesture](/docs/core-components/gesture-detectors#views-reordered-during-a-gesture) for details.
314+
315+
<CodeComparison
316+
label1={"RNGH 2"}
317+
label2={"RNGH 3"}
318+
code1={
319+
`
320+
function Row() {
321+
const pan = Gesture.Pan();
322+
323+
return (
324+
<GestureDetector gesture={pan}>
325+
<View />
326+
</GestureDetector>
327+
);
328+
}
329+
330+
<FlatList renderItem={() => <Row />} />
331+
`}
332+
code2={
333+
`
334+
function Row() {
335+
const pan = usePanGesture();
336+
337+
return (
338+
<VirtualGestureDetector gesture={pan}>
339+
<View />
340+
</VirtualGestureDetector>
341+
);
342+
}
343+
344+
<InterceptingGestureDetector>
345+
<FlatList renderItem={() => <Row />} />
346+
</InterceptingGestureDetector>
347+
`}
348+
/>
349+
311350
## Old components
312351

313352
### Buttons

0 commit comments

Comments
 (0)