From f5af4bd2b7706388301152ef0b24b531219a0ff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 13 Mar 2026 11:15:39 +0100 Subject: [PATCH 01/10] web --- .../src/v3/gestureStateManager.ts | 6 ------ .../src/v3/gestureStateManager.web.ts | 15 --------------- .../src/web/handlers/GestureHandler.ts | 2 +- 3 files changed, 1 insertion(+), 22 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/gestureStateManager.ts b/packages/react-native-gesture-handler/src/v3/gestureStateManager.ts index a6c6c68032..62ac181824 100644 --- a/packages/react-native-gesture-handler/src/v3/gestureStateManager.ts +++ b/packages/react-native-gesture-handler/src/v3/gestureStateManager.ts @@ -2,7 +2,6 @@ import { State } from '../State'; import { tagMessage } from '../utils'; export type GestureStateManagerType = { - begin(handlerTag: number): void; activate(handlerTag: number): void; fail(handlerTag: number): void; deactivate(handlerTag: number): void; @@ -21,11 +20,6 @@ const setGestureState = (handlerTag: number, state: State) => { }; export const GestureStateManager: GestureStateManagerType = { - begin(handlerTag: number) { - 'worklet'; - setGestureState(handlerTag, State.BEGAN); - }, - activate(handlerTag: number) { 'worklet'; setGestureState(handlerTag, State.ACTIVE); diff --git a/packages/react-native-gesture-handler/src/v3/gestureStateManager.web.ts b/packages/react-native-gesture-handler/src/v3/gestureStateManager.web.ts index 8132cafce0..2be501eec2 100644 --- a/packages/react-native-gesture-handler/src/v3/gestureStateManager.web.ts +++ b/packages/react-native-gesture-handler/src/v3/gestureStateManager.web.ts @@ -1,4 +1,3 @@ -import { State } from '../State'; import { tagMessage } from '../utils'; import IGestureHandler from '../web/handlers/IGestureHandler'; import GestureHandlerOrchestrator from '../web/tools/GestureHandlerOrchestrator'; @@ -16,24 +15,10 @@ function ensureHandlerAttached(handler: IGestureHandler) { } export const GestureStateManager: GestureStateManagerType = { - begin(handlerTag: number): void { - 'worklet'; - const handler = NodeManager.getHandler(handlerTag); - ensureHandlerAttached(handler); - - GestureHandlerOrchestrator.instance.recordHandlerIfNotPresent(handler); - handler.begin(); - }, - activate(handlerTag: number): void { 'worklet'; const handler = NodeManager.getHandler(handlerTag); ensureHandlerAttached(handler); - // Force going from UNDETERMINED to ACTIVE through BEGAN to preserve - // the correct state transition flow. - if (handler.state === State.UNDETERMINED) { - handler.begin(); - } GestureHandlerOrchestrator.instance.recordHandlerIfNotPresent(handler); handler.activate(true); diff --git a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts index a4692ad0b3..77e6166fa8 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts @@ -228,7 +228,7 @@ export default abstract class GestureHandler implements IGestureHandler { public activate(force = false) { if ( (this.manualActivation !== true || force) && - (this.state === State.UNDETERMINED || this.state === State.BEGAN) + this.state === State.BEGAN ) { this.delegate.onActivate(); this.moveToState(State.ACTIVE); From 7d056906bef9dbe20ba531542c258925e8e7956b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 13 Mar 2026 12:17:43 +0100 Subject: [PATCH 02/10] android --- .../swmansion/gesturehandler/react/RNGestureHandlerModule.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt index 7b2ab98640..370e75ea16 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt @@ -148,10 +148,9 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) : if (handler.state == GestureHandler.STATE_UNDETERMINED) { handler.forceReinitializeDuringOnHandle = true - // When going from UNDETERMINED to ACTIVE, force going through BEGAN to preserve - // the correct state flow + // We don't allow activation of gestures which haven't received any touches if (newState == GestureHandler.STATE_ACTIVE) { - handler.begin() + return } } From 3f03704368ed568862d8db65bd55a60db1be7839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 13 Mar 2026 12:38:03 +0100 Subject: [PATCH 03/10] fix android --- .../gesturehandler/react/RNGestureHandlerModule.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt index 370e75ea16..adc6426382 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt @@ -145,17 +145,17 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) : UiThreadUtil.assertOnUiThread() registry.getHandler(handlerTag)?.let { handler -> - if (handler.state == GestureHandler.STATE_UNDETERMINED) { - handler.forceReinitializeDuringOnHandle = true + if (newState == GestureHandler.STATE_ACTIVE || newState == GestureHandler.STATE_BEGAN) { + handler.recordHandlerIfNotPresent() + } + if (handler.state == GestureHandler.STATE_UNDETERMINED) { // We don't allow activation of gestures which haven't received any touches if (newState == GestureHandler.STATE_ACTIVE) { return } - } - if (newState == GestureHandler.STATE_ACTIVE || newState == GestureHandler.STATE_BEGAN) { - handler.recordHandlerIfNotPresent() + handler.forceReinitializeDuringOnHandle = true } when (newState) { From d29c37773927f5526f4cc0b865c90de9c6e13a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 13 Mar 2026 13:11:07 +0100 Subject: [PATCH 04/10] ios --- .../apple/RNGestureHandler.h | 11 ++++++++--- .../apple/RNGestureHandlerModule.mm | 6 +++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandler.h b/packages/react-native-gesture-handler/apple/RNGestureHandler.h index 349c8ffd21..f4de60db5f 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandler.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandler.h @@ -86,6 +86,7 @@ @property (nonatomic, weak, nullable) RNGHUIView *hostDetectorView; @property (nonatomic, nullable, assign) NSNumber *virtualViewTag; @property (nonatomic, copy, nullable) NSNumber *viewTag; +@property (nonatomic, readonly) RNGestureHandlerState lastState; - (BOOL)isViewParagraphComponent:(nullable RNGHUIView *)view; - (nonnull RNGHUIView *)chooseViewForInteraction:(nonnull UIGestureRecognizer *)recognizer; @@ -97,9 +98,13 @@ - (void)updateRelations:(nonnull NSDictionary *)relations; - (void)handleGesture:(nonnull id)recognizer; - (void)handleGesture:(nonnull id)recognizer fromReset:(BOOL)fromReset; -- (void)handleGesture:(nonnull id)recognizer fromReset:(BOOL)fromReset fromManualStateChange:(BOOL)fromManualStateChange; +- (void)handleGesture:(nonnull id)recognizer + fromReset:(BOOL)fromReset + fromManualStateChange:(BOOL)fromManualStateChange; - (void)handleGesture:(nonnull id)recognizer inState:(RNGestureHandlerState)state; -- (void)handleGesture:(nonnull id)recognizer inState:(RNGestureHandlerState)state fromManualStateChange:(BOOL)fromManualStateChange; +- (void)handleGesture:(nonnull id)recognizer + inState:(RNGestureHandlerState)state + fromManualStateChange:(BOOL)fromManualStateChange; - (BOOL)containsPointInView; - (RNGestureHandlerState)state; - (nullable RNGestureHandlerEventExtraData *)eventExtraData:(nonnull id)recognizer; @@ -112,7 +117,7 @@ - (void)sendEventsInState:(RNGestureHandlerState)state forViewWithTag:(nonnull NSNumber *)reactTag withExtraData:(nonnull RNGestureHandlerEventExtraData *)extraData - fromManualStateChange:(BOOL)fromManualStateChange; + fromManualStateChange:(BOOL)fromManualStateChange; - (void)sendEvent:(nonnull RNGestureHandlerStateChange *)event; - (void)sendTouchEventInState:(RNGestureHandlerState)state forViewWithTag:(nonnull NSNumber *)reactTag; - (nullable RNGHUIScrollView *)retrieveScrollView:(nonnull RNGHUIView *)view; diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm index 8be6f2a08d..d284a4d6b4 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm @@ -225,9 +225,9 @@ - (void)setGestureStateSync:(int)state forHandler:(int)handlerTag } else if (state == 3) { // CANCELLED handler.recognizer.state = RNGHGestureRecognizerStateCancelled; } else if (state == 4) { // ACTIVE - if (handler.recognizer.state == UIGestureRecognizerStatePossible) { - // Force going from UNDETERMINED to ACTIVE through BEGAN to preserve the correct state transition flow. - [handler handleGesture:handler.recognizer fromReset:NO fromManualStateChange:YES]; + // We don't allow activation of gestures which haven't received any touches + if (handler.lastState == RNGestureHandlerStateUndetermined) { + return; } [handler stopActivationBlocker]; handler.recognizer.state = RNGHGestureRecognizerStateBegan; From 44459c32e65692e5f6d8df2a6b4803d6bb9855cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 13 Mar 2026 14:32:39 +0100 Subject: [PATCH 05/10] update docs --- .../docs/fundamentals/state-manager.mdx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx b/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx index ab685ff241..e822b1d970 100644 --- a/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx +++ b/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx @@ -68,7 +68,7 @@ const styles = StyleSheet.create({ ### Outside gesture definition -If you want to control gesture lifecycle outside of it, you can use `handlerTag` from created gesture object. +If you want to control gesture lifecycle outside of it, you can use `handlerTag` from created gesture object. Note that gestures can only be activated after it has begun, that is, after it has received received touch events. void; -``` - -Triggers [`onBegin`](/docs/fundamentals/callbacks-events#onbegin) callback on gesture with specified `handlerTag`. - ### activate ```tsx From ade036760988f9a22ae13054966ab93e6c7d8468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 13 Mar 2026 15:45:18 +0100 Subject: [PATCH 06/10] update skill --- skills/gesture-handler-3-migration/SKILL.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/skills/gesture-handler-3-migration/SKILL.md b/skills/gesture-handler-3-migration/SKILL.md index 888238fe2e..8c6aed99d1 100644 --- a/skills/gesture-handler-3-migration/SKILL.md +++ b/skills/gesture-handler-3-migration/SKILL.md @@ -69,7 +69,6 @@ In Gesture Handler 3, `stateManager` is no longer passed to `TouchEvent` callbac `GestureStateManager` provides methods for imperative state management: -- .begin(handlerTag: number) - .activate(handlerTag: number) - .deactivate(handlerTag: number) (.end() in the old API) - .fail(handlerTag: number) @@ -81,6 +80,8 @@ In Gesture Handler 3, `stateManager` is no longer passed to `TouchEvent` callbac Callback definitions CANNOT reference the gesture that's being defined. In this scenario use events to get access to the handler tag. +Gesture cannot be activated via the StateManager if has not begun, that is, after it has received received touch events. + ### Migrating relations #### Composed gestures From 769ac67a50d0619947fc7007c61ceb5c4f3484e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 13 Mar 2026 16:32:20 +0100 Subject: [PATCH 07/10] cleaner android --- .../gesturehandler/react/RNGestureHandlerModule.kt | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt index adc6426382..986f5c1e13 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt @@ -145,17 +145,12 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) : UiThreadUtil.assertOnUiThread() registry.getHandler(handlerTag)?.let { handler -> - if (newState == GestureHandler.STATE_ACTIVE || newState == GestureHandler.STATE_BEGAN) { - handler.recordHandlerIfNotPresent() - } - - if (handler.state == GestureHandler.STATE_UNDETERMINED) { - // We don't allow activation of gestures which haven't received any touches - if (newState == GestureHandler.STATE_ACTIVE) { + if (newState == GestureHandler.STATE_ACTIVE) { + if (handler.state != GestureHandler.STATE_BEGAN) { + // We don't allow activation of gestures which haven't received any touches return } - - handler.forceReinitializeDuringOnHandle = true + handler.recordHandlerIfNotPresent() } when (newState) { From 6d018db7b2af977736a4c5e6a324fc2e322ad907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= <81448793+akwasniewski@users.noreply.github.com> Date: Tue, 17 Mar 2026 09:39:31 +0100 Subject: [PATCH 08/10] Update packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: MichaƂ Bert <63123542+m-bert@users.noreply.github.com> --- .../docs-gesture-handler/docs/fundamentals/state-manager.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx b/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx index e822b1d970..bf77c15e53 100644 --- a/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx +++ b/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx @@ -68,7 +68,7 @@ const styles = StyleSheet.create({ ### Outside gesture definition -If you want to control gesture lifecycle outside of it, you can use `handlerTag` from created gesture object. Note that gestures can only be activated after it has begun, that is, after it has received received touch events. +If you want to control gesture lifecycle outside of it, you can use `handlerTag` from created gesture object. Note that gesture can only be activated after it has begun, that is, after it has received received touch events. Date: Tue, 17 Mar 2026 09:44:34 +0100 Subject: [PATCH 09/10] note --- .../docs/fundamentals/state-manager.mdx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx b/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx index bf77c15e53..605d9cd833 100644 --- a/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx +++ b/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx @@ -68,7 +68,11 @@ const styles = StyleSheet.create({ ### Outside gesture definition -If you want to control gesture lifecycle outside of it, you can use `handlerTag` from created gesture object. Note that gesture can only be activated after it has begun, that is, after it has received received touch events. +If you want to control gesture lifecycle outside of it, you can use `handlerTag` from created gesture object. + +:::note +The gestures can only be activated after it has begun, that is, after it has received received touch events. +::: Date: Tue, 17 Mar 2026 09:56:30 +0100 Subject: [PATCH 10/10] update skill --- skills/gesture-handler-3-migration/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/gesture-handler-3-migration/SKILL.md b/skills/gesture-handler-3-migration/SKILL.md index 8c6aed99d1..2ffc89b111 100644 --- a/skills/gesture-handler-3-migration/SKILL.md +++ b/skills/gesture-handler-3-migration/SKILL.md @@ -80,7 +80,7 @@ In Gesture Handler 3, `stateManager` is no longer passed to `TouchEvent` callbac Callback definitions CANNOT reference the gesture that's being defined. In this scenario use events to get access to the handler tag. -Gesture cannot be activated via the StateManager if has not begun, that is, after it has received received touch events. +Remove GestureStateManager.begin() as gestures must now automatically enter the BEGAN state via touch events before they can be activated through the GestureStateManager. ### Migrating relations