Skip to content

Commit c415c99

Browse files
j-piaseckiCopilot
andauthored
[Web] Don't call cancel on unregistered handlers (#4134)
## Description On web, it was possible for a handler to get canceled twice: once by some internal condition, and once by `pointercancel` event. The first time was handled correctly, since the orchestrator tracked the gesture, and it was reset correctly after being canceled. The second time, the gesture was untracked after being reset and it was canceled "in the void" and it was kept in the canceled state until it was registered again, which allowed it to be cleaned properly. It's easy to reproduce with a `Touchable` inside a `ScrollView` and starting to scroll near the very edge of the touchable. The first cancel happens when the pointer moves out of the touchable before the scroll starts, and the second one is caused by the scroll starting. This PR changes the behavior so that handlers need to be registered in the orchestrator before being canceled. ## Test plan |Before|After| |-|-| |<video src="https://github.com/user-attachments/assets/5534bcd7-f160-4c6c-9fc9-53643364ca95" />|<video src="https://github.com/user-attachments/assets/31a6a53c-dfa3-427b-bc8e-0e74f99ff2f9" />| --------- Co-authored-by: Copilot <copilot@github.com>
1 parent 9c6f72c commit c415c99

2 files changed

Lines changed: 8 additions & 2 deletions

File tree

packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,9 @@ export default abstract class GestureHandler implements IGestureHandler {
362362
protected onPointerCancel(_event: AdaptedEvent): void {
363363
// No need to send a cancel touch event explicitly here. `cancel` will
364364
// handle cancelling all tracked touches if the handler expects pointer data.
365-
this.cancel();
365+
if (GestureHandlerOrchestrator.instance.isHandlerRecorded(this)) {
366+
this.cancel();
367+
}
366368
}
367369
protected onPointerOutOfBounds(event: AdaptedEvent): void {
368370
this.tryToSendMoveEvent(true, event);

packages/react-native-gesture-handler/src/web/tools/GestureHandlerOrchestrator.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ export default class GestureHandlerOrchestrator {
3232
handler.activationIndex = Number.MAX_VALUE;
3333
}
3434

35+
public isHandlerRecorded(handler: IGestureHandler): boolean {
36+
return this.gestureHandlers.includes(handler);
37+
}
38+
3539
public removeHandlerFromOrchestrator(handler: IGestureHandler): void {
3640
const indexInGestureHandlers = this.gestureHandlers.indexOf(handler);
3741
const indexInAwaitingHandlers = this.awaitingHandlers.indexOf(handler);
@@ -266,7 +270,7 @@ export default class GestureHandlerOrchestrator {
266270
}
267271

268272
public recordHandlerIfNotPresent(handler: IGestureHandler): void {
269-
if (this.gestureHandlers.includes(handler)) {
273+
if (this.isHandlerRecorded(handler)) {
270274
return;
271275
}
272276

0 commit comments

Comments
 (0)