diff --git a/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx b/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx index ab685ff241..605d9cd833 100644 --- a/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx +++ b/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx @@ -70,6 +70,10 @@ const styles = StyleSheet.create({ 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. +::: + void; -``` - -Triggers [`onBegin`](/docs/fundamentals/callbacks-events#onbegin) callback on gesture with specified `handlerTag`. - ### activate ```tsx 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..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,11 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) : UiThreadUtil.assertOnUiThread() registry.getHandler(handlerTag)?.let { handler -> - 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 - if (newState == GestureHandler.STATE_ACTIVE) { - handler.begin() + 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 } - } - - if (newState == GestureHandler.STATE_ACTIVE || newState == GestureHandler.STATE_BEGAN) { handler.recordHandlerIfNotPresent() } diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandler.h b/packages/react-native-gesture-handler/apple/RNGestureHandler.h index 9ab60f35bf..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; 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; 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); diff --git a/skills/gesture-handler-3-migration/SKILL.md b/skills/gesture-handler-3-migration/SKILL.md index 888238fe2e..2ffc89b111 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. +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 #### Composed gestures