From 08fc77d16b09f5e039c4157800b96c672e02bf03 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 18 Dec 2025 12:01:36 +0100 Subject: [PATCH 1/6] Move end flag to event --- .../src/components/switchAndInput/index.tsx | 9 +-- .../src/v3/components/GestureButtons.tsx | 9 +-- .../src/v3/hooks/callbacks/eventHandler.ts | 24 ++++--- .../v3/hooks/callbacks/stateChangeHandler.ts | 65 ------------------- .../v3/hooks/callbacks/touchEventHandler.ts | 32 --------- .../src/v3/hooks/callbacks/updateHandler.ts | 53 --------------- .../src/v3/hooks/utils/eventHandlersUtils.ts | 14 ++-- .../src/v3/types/ConfigTypes.ts | 10 +-- .../src/v3/types/EventTypes.ts | 4 ++ .../src/v3/types/index.ts | 2 +- 10 files changed, 33 insertions(+), 189 deletions(-) delete mode 100644 packages/react-native-gesture-handler/src/v3/hooks/callbacks/stateChangeHandler.ts delete mode 100644 packages/react-native-gesture-handler/src/v3/hooks/callbacks/touchEventHandler.ts delete mode 100644 packages/react-native-gesture-handler/src/v3/hooks/callbacks/updateHandler.ts diff --git a/apps/common-app/src/components/switchAndInput/index.tsx b/apps/common-app/src/components/switchAndInput/index.tsx index e5a114f7d2..63f3129c5c 100644 --- a/apps/common-app/src/components/switchAndInput/index.tsx +++ b/apps/common-app/src/components/switchAndInput/index.tsx @@ -1,6 +1,5 @@ import React, { useState } from 'react'; import { StyleSheet, Text, View } from 'react-native'; -import type { NativeGestureEvent } from 'react-native-gesture-handler'; import { TextInput, LegacyTextInput, @@ -22,8 +21,8 @@ export default function SwitchTextInputExample() { console.log('[TextInput] onBegin')} onActivate={() => console.log('[TextInput] onActivate')} - onFinalize={(_: NativeGestureEvent, s: boolean) => - console.log('[TextInput] onFinalize', s) + onFinalize={(e) => + console.log('[TextInput] onFinalize', e.canceled) } style={styles.input} placeholder="Type here..." @@ -50,9 +49,7 @@ export default function SwitchTextInputExample() { console.log('[Switch] onBegin')} - onFinalize={(_: NativeGestureEvent, s: boolean) => - console.log('[Switch] onFinalize', s) - } + onFinalize={(e) => console.log('[Switch] onFinalize', e.canceled)} value={switchOn} onValueChange={(v: boolean) => { console.log('[Switch] onValueChange', v); diff --git a/packages/react-native-gesture-handler/src/v3/components/GestureButtons.tsx b/packages/react-native-gesture-handler/src/v3/components/GestureButtons.tsx index 4da169ac42..894703179c 100644 --- a/packages/react-native-gesture-handler/src/v3/components/GestureButtons.tsx +++ b/packages/react-native-gesture-handler/src/v3/components/GestureButtons.tsx @@ -8,10 +8,11 @@ import type { RectButtonProps, } from './GestureButtonsProps'; -import type { GestureEvent } from '../types'; +import type { GestureEndEvent, GestureEvent } from '../types'; import type { NativeViewHandlerData } from '../hooks/gestures/native/useNativeGesture'; type CallbackEventType = GestureEvent; +type EndCallbackEventType = GestureEndEvent; export const RawButton = createNativeWrapper(GestureHandlerButton, { shouldCancelWhenOutside: false, @@ -58,15 +59,15 @@ export const BaseButton = (props: BaseButtonProps) => { } }; - const onDeactivate = (e: CallbackEventType, success: boolean) => { + const onDeactivate = (e: EndCallbackEventType) => { onActiveStateChange?.(false); - if (success && !longPressDetected.current) { + if (!e.canceled && !longPressDetected.current) { onPress?.(e.pointerInside); } }; - const onFinalize = (_e: CallbackEventType) => { + const onFinalize = (_e: EndCallbackEventType) => { if (longPressTimeout.current !== undefined) { clearTimeout(longPressTimeout.current); longPressTimeout.current = undefined; diff --git a/packages/react-native-gesture-handler/src/v3/hooks/callbacks/eventHandler.ts b/packages/react-native-gesture-handler/src/v3/hooks/callbacks/eventHandler.ts index b01b90fd84..91c911c3d6 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/callbacks/eventHandler.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/callbacks/eventHandler.ts @@ -10,6 +10,7 @@ import { ReanimatedContext } from '../../../handlers/gestures/reanimatedWrapper' import { ChangeCalculatorType, GestureCallbacks, + GestureEndEvent, GestureHandlerEventWithHandlerData, GestureStateChangeEventWithHandlerData, GestureUpdateEventWithHandlerData, @@ -35,23 +36,20 @@ function handleStateChangeEvent( state === State.ACTIVE ) { runCallback(CALLBACK_TYPE.START, callbacks, event); - } else if (oldState !== state && state === State.END) { - if (oldState === State.ACTIVE) { - runCallback(CALLBACK_TYPE.END, callbacks, event, true); - } - runCallback(CALLBACK_TYPE.FINALIZE, callbacks, event, true); - - if (context) { - context.lastUpdateEvent = undefined; - } } else if ( - (state === State.FAILED || state === State.CANCELLED) && - state !== oldState + oldState !== state && + (state === State.END || state === State.FAILED || state === State.CANCELLED) ) { + const canceled = state === State.FAILED || state === State.CANCELLED; + const endEvent: GestureEndEvent = { + ...event, + canceled, + }; + if (oldState === State.ACTIVE) { - runCallback(CALLBACK_TYPE.END, callbacks, event, false); + runCallback(CALLBACK_TYPE.END, callbacks, endEvent); } - runCallback(CALLBACK_TYPE.FINALIZE, callbacks, event, false); + runCallback(CALLBACK_TYPE.FINALIZE, callbacks, endEvent); if (context) { context.lastUpdateEvent = undefined; diff --git a/packages/react-native-gesture-handler/src/v3/hooks/callbacks/stateChangeHandler.ts b/packages/react-native-gesture-handler/src/v3/hooks/callbacks/stateChangeHandler.ts deleted file mode 100644 index d5d93ed785..0000000000 --- a/packages/react-native-gesture-handler/src/v3/hooks/callbacks/stateChangeHandler.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { ReanimatedContext } from '../../../handlers/gestures/reanimatedWrapper'; -import { CALLBACK_TYPE } from '../../../handlers/gestures/gesture'; -import { State } from '../../../State'; -import { - GestureCallbacks, - GestureStateChangeEventWithHandlerData, - StateChangeEventWithHandlerData, -} from '../../types'; -import { - flattenAndFilterEvent, - isEventForHandlerWithTag, - maybeExtractNativeEvent, - runCallback, -} from '../utils'; - -export function getStateChangeHandler( - handlerTag: number, - callbacks: GestureCallbacks, - context?: ReanimatedContext -) { - return (sourceEvent: StateChangeEventWithHandlerData) => { - 'worklet'; - - const eventWithData = maybeExtractNativeEvent( - sourceEvent - ) as GestureStateChangeEventWithHandlerData; - const event = flattenAndFilterEvent(eventWithData); - - if (!isEventForHandlerWithTag(handlerTag, eventWithData)) { - return; - } - - const { state, oldState } = eventWithData; - - if (oldState === State.UNDETERMINED && state === State.BEGAN) { - runCallback(CALLBACK_TYPE.BEGAN, callbacks, event); - } else if ( - (oldState === State.BEGAN || oldState === State.UNDETERMINED) && - state === State.ACTIVE - ) { - runCallback(CALLBACK_TYPE.START, callbacks, event); - } else if (oldState !== state && state === State.END) { - if (oldState === State.ACTIVE) { - runCallback(CALLBACK_TYPE.END, callbacks, event, true); - } - runCallback(CALLBACK_TYPE.FINALIZE, callbacks, event, true); - - if (context) { - context.lastUpdateEvent = undefined; - } - } else if ( - (state === State.FAILED || state === State.CANCELLED) && - state !== oldState - ) { - if (oldState === State.ACTIVE) { - runCallback(CALLBACK_TYPE.END, callbacks, event, false); - } - runCallback(CALLBACK_TYPE.FINALIZE, callbacks, event, false); - - if (context) { - context.lastUpdateEvent = undefined; - } - } - }; -} diff --git a/packages/react-native-gesture-handler/src/v3/hooks/callbacks/touchEventHandler.ts b/packages/react-native-gesture-handler/src/v3/hooks/callbacks/touchEventHandler.ts deleted file mode 100644 index ef65483218..0000000000 --- a/packages/react-native-gesture-handler/src/v3/hooks/callbacks/touchEventHandler.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { GestureCallbacks, TouchEvent } from '../../types'; -import { - isEventForHandlerWithTag, - maybeExtractNativeEvent, - runCallback, - touchEventTypeToCallbackType, -} from '../utils'; -import { TouchEventType } from '../../../TouchEventType'; -import { GestureTouchEvent } from '../../../handlers/gestureHandlerCommon'; - -export function getTouchEventHandler( - handlerTag: number, - callbacks: GestureCallbacks -) { - return (sourceEvent: TouchEvent) => { - 'worklet'; - - const event = maybeExtractNativeEvent(sourceEvent) as GestureTouchEvent; - - if (!isEventForHandlerWithTag(handlerTag, event)) { - return; - } - - if (event.eventType !== TouchEventType.UNDETERMINED) { - runCallback( - touchEventTypeToCallbackType(event.eventType), - callbacks, - event - ); - } - }; -} diff --git a/packages/react-native-gesture-handler/src/v3/hooks/callbacks/updateHandler.ts b/packages/react-native-gesture-handler/src/v3/hooks/callbacks/updateHandler.ts deleted file mode 100644 index a59c9e3621..0000000000 --- a/packages/react-native-gesture-handler/src/v3/hooks/callbacks/updateHandler.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { CALLBACK_TYPE } from '../../../handlers/gestures/gesture'; -import { tagMessage } from '../../../utils'; -import { ReanimatedContext } from '../../../handlers/gestures/reanimatedWrapper'; -import { - ChangeCalculatorType, - GestureCallbacks, - GestureUpdateEventWithHandlerData, - UpdateEventWithHandlerData, -} from '../../types'; -import { - flattenAndFilterEvent, - isEventForHandlerWithTag, - maybeExtractNativeEvent, - runCallback, -} from '../utils'; - -export function getUpdateHandler( - handlerTag: number, - callbacks: GestureCallbacks, - context: ReanimatedContext | undefined, - changeEventCalculator?: ChangeCalculatorType -) { - return (sourceEvent: UpdateEventWithHandlerData) => { - 'worklet'; - - const eventWithData = maybeExtractNativeEvent( - sourceEvent - ) as GestureUpdateEventWithHandlerData; - - const eventWithChanges = changeEventCalculator - ? changeEventCalculator( - eventWithData, - context ? context.lastUpdateEvent : undefined - ) - : eventWithData; - - const event = flattenAndFilterEvent(eventWithChanges); - - if (!isEventForHandlerWithTag(handlerTag, eventWithData)) { - return; - } - - // This should never happen, but since we don't want to call hooks conditionally, we have to mark - // context as possibly undefined to make TypeScript happy. - if (!context) { - throw new Error(tagMessage('Event handler context is not defined')); - } - - runCallback(CALLBACK_TYPE.UPDATE, callbacks, event); - - context.lastUpdateEvent = eventWithData; - }; -} diff --git a/packages/react-native-gesture-handler/src/v3/hooks/utils/eventHandlersUtils.ts b/packages/react-native-gesture-handler/src/v3/hooks/utils/eventHandlersUtils.ts index 3c7da5c433..0d2263858e 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/utils/eventHandlersUtils.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/utils/eventHandlersUtils.ts @@ -4,10 +4,10 @@ import { CALLBACK_TYPE } from '../../../handlers/gestures/gesture'; import { GestureCallbacks, GestureEventCallback, - GestureEventCallbackWithDidSucceed, GestureTouchEventCallback, UnpackedGestureHandlerEvent, } from '../../types'; +import { GestureEndEventCallback } from '../../types/ConfigTypes'; export function useMemoizedGestureCallbacks( callbacks: GestureCallbacks @@ -51,7 +51,7 @@ function getHandler( callbacks: GestureCallbacks ): | GestureEventCallback - | GestureEventCallbackWithDidSucceed + | GestureEndEventCallback | GestureTouchEventCallback | undefined { 'worklet'; @@ -95,13 +95,11 @@ export function touchEventTypeToCallbackType( } type SingleParameterCallback = (event: T) => void; -type DoubleParameterCallback = (event: T, didSucceed: boolean) => void; export function runCallback( type: CALLBACK_TYPE, callbacks: GestureCallbacks, - event: UnpackedGestureHandlerEvent, - didSucceed?: boolean + event: UnpackedGestureHandlerEvent ) { 'worklet'; const handler = getHandler(type, callbacks); @@ -110,9 +108,5 @@ export function runCallback( return; } - if (didSucceed === undefined) { - (handler as SingleParameterCallback)(event); - } else { - (handler as DoubleParameterCallback)(event, didSucceed); - } + (handler as SingleParameterCallback)(event); } diff --git a/packages/react-native-gesture-handler/src/v3/types/ConfigTypes.ts b/packages/react-native-gesture-handler/src/v3/types/ConfigTypes.ts index fabcdb2bf3..3f6f0822d5 100644 --- a/packages/react-native-gesture-handler/src/v3/types/ConfigTypes.ts +++ b/packages/react-native-gesture-handler/src/v3/types/ConfigTypes.ts @@ -9,6 +9,7 @@ import { import { AnimatedEvent, ChangeCalculatorType, + GestureEndEvent, GestureEvent, } from './EventTypes'; import { WithSharedValue } from './ReanimatedTypes'; @@ -17,9 +18,8 @@ export type GestureEventCallback = ( event: GestureEvent ) => void; -export type GestureEventCallbackWithDidSucceed = ( - event: GestureEvent, - didSucceed: boolean +export type GestureEndEventCallback = ( + event: GestureEndEvent ) => void; export type GestureTouchEventCallback = (event: GestureTouchEvent) => void; @@ -27,8 +27,8 @@ export type GestureTouchEventCallback = (event: GestureTouchEvent) => void; export type GestureCallbacks = { onBegin?: GestureEventCallback; onActivate?: GestureEventCallback; - onDeactivate?: GestureEventCallbackWithDidSucceed; - onFinalize?: GestureEventCallbackWithDidSucceed; + onDeactivate?: GestureEndEventCallback; + onFinalize?: GestureEndEventCallback; onUpdate?: GestureEventCallback | AnimatedEvent; onTouchesDown?: GestureTouchEventCallback; onTouchesMove?: GestureTouchEventCallback; diff --git a/packages/react-native-gesture-handler/src/v3/types/EventTypes.ts b/packages/react-native-gesture-handler/src/v3/types/EventTypes.ts index 79d2643cd1..2ed81be3c3 100644 --- a/packages/react-native-gesture-handler/src/v3/types/EventTypes.ts +++ b/packages/react-native-gesture-handler/src/v3/types/EventTypes.ts @@ -53,6 +53,10 @@ export type GestureEvent = { handlerTag: number; } & HandlerData; +export type GestureEndEvent = { + canceled: boolean; +} & GestureEvent; + export type UnpackedGestureHandlerEvent = | GestureEvent | GestureTouchEvent; diff --git a/packages/react-native-gesture-handler/src/v3/types/index.ts b/packages/react-native-gesture-handler/src/v3/types/index.ts index fc94570b41..7f6f9ae9c6 100644 --- a/packages/react-native-gesture-handler/src/v3/types/index.ts +++ b/packages/react-native-gesture-handler/src/v3/types/index.ts @@ -15,6 +15,7 @@ export type { StateChangeEventWithHandlerData, TouchEvent, GestureEvent, + GestureEndEvent, AnimatedEvent, ChangeCalculatorType, DiffCalculatorType, @@ -23,7 +24,6 @@ export type { export type { GestureCallbacks, GestureEventCallback, - GestureEventCallbackWithDidSucceed, GestureTouchEventCallback, GestureRelations, InternalConfigProps, From a753003f3c401d98d1efda69a33ba2c11c7295e5 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Tue, 14 Apr 2026 11:50:49 +0200 Subject: [PATCH 2/6] Fix examples --- .../src/new_api/hover_mouse/hover/index.tsx | 4 ++-- .../src/new_api/showcase/overlap/index.tsx | 16 ++++++++-------- .../src/new_api/simple/longPress/index.tsx | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/common-app/src/new_api/hover_mouse/hover/index.tsx b/apps/common-app/src/new_api/hover_mouse/hover/index.tsx index ba3e4be77a..138c9cf1e1 100644 --- a/apps/common-app/src/new_api/hover_mouse/hover/index.tsx +++ b/apps/common-app/src/new_api/hover_mouse/hover/index.tsx @@ -29,8 +29,8 @@ function useColoredHover( onActivate: () => { console.log('hover start', color); }, - onDeactivate: (_, success) => { - console.log('hover end', color, 'failed', !success); + onDeactivate: (e) => { + console.log('hover end', color, 'failed', e.canceled); }, onFinalize: () => { hovered.value = false; diff --git a/apps/common-app/src/new_api/showcase/overlap/index.tsx b/apps/common-app/src/new_api/showcase/overlap/index.tsx index fa2cd8c32d..d2af2bcece 100644 --- a/apps/common-app/src/new_api/showcase/overlap/index.tsx +++ b/apps/common-app/src/new_api/showcase/overlap/index.tsx @@ -31,8 +31,8 @@ function OverlapSiblings() { const [elevated, setElevated] = React.useState(''); const tapPurple = useTapGesture({ - onDeactivate: (_e, success) => { - if (success) { + onDeactivate: (e) => { + if (!e.canceled) { setElevated('purple'); feedbackRef.current?.showMessage('Tapped purple'); } @@ -41,8 +41,8 @@ function OverlapSiblings() { }); const tapBlue = useTapGesture({ - onDeactivate: (_e, success) => { - if (success) { + onDeactivate: (e) => { + if (!e.canceled) { setElevated('blue'); feedbackRef.current?.showMessage('Tapped blue'); } @@ -75,8 +75,8 @@ function OverlapParents() { const [elevated, setElevated] = React.useState(''); const tapRed = useTapGesture({ - onDeactivate: (_e, success) => { - if (success) { + onDeactivate: (e) => { + if (!e.canceled) { feedbackRef.current?.showMessage('Tapped purple'); setElevated('purple'); } @@ -85,8 +85,8 @@ function OverlapParents() { }); const tapGreen = useTapGesture({ - onDeactivate: (_e, success) => { - if (success) { + onDeactivate: (e) => { + if (!e.canceled) { feedbackRef.current?.showMessage('Tapped blue'); setElevated('blue'); } diff --git a/apps/common-app/src/new_api/simple/longPress/index.tsx b/apps/common-app/src/new_api/simple/longPress/index.tsx index 90902d4df7..06ab1ea266 100644 --- a/apps/common-app/src/new_api/simple/longPress/index.tsx +++ b/apps/common-app/src/new_api/simple/longPress/index.tsx @@ -38,8 +38,8 @@ export default function LongPressExample() { duration: 100, }); }, - onFinalize: (_, success) => { - finalise_color.value = success ? COLORS.GREEN : COLORS.RED; + onFinalize: (e) => { + finalise_color.value = e.canceled ? COLORS.RED : COLORS.GREEN; colorProgress.value = 1; colorProgress.value = withTiming( 0, From 135d24d23ad1359acb02eb818fa3995e55747a16 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Tue, 14 Apr 2026 13:44:15 +0200 Subject: [PATCH 3/6] Update docs --- .../docs/fundamentals/callbacks-events.mdx | 18 +++++++++---- .../_shared/base-gesture-callbacks.mdx | 8 +++--- .../docs/guides/upgrading-to-3.mdx | 26 +++++++++++++++++++ .../docs/under-the-hood/state.md | 6 ++--- skills/gesture-handler-3-migration/SKILL.md | 14 ++++++++++ 5 files changed, 60 insertions(+), 12 deletions(-) diff --git a/packages/docs-gesture-handler/docs/fundamentals/callbacks-events.mdx b/packages/docs-gesture-handler/docs/fundamentals/callbacks-events.mdx index 9dc9d10f33..cb41739995 100644 --- a/packages/docs-gesture-handler/docs/fundamentals/callbacks-events.mdx +++ b/packages/docs-gesture-handler/docs/fundamentals/callbacks-events.mdx @@ -55,18 +55,18 @@ Called each time a pointer tracked by the gesture changes state, typically due t ### onDeactivate ```ts -onDeactivate: (event: GestureEvent, didSucceed: boolean) => void +onDeactivate: (event: GestureEndEvent) => void ``` -Called when handler stops recognizing gestures, but only if the handler activated. It is called before `onFinalize`. If the handler was interrupted, the `didSucceed` argument is set to `false`. Otherwise it is set to `true`. +Called when handler stops recognizing gestures, but only if the handler activated. It is called before `onFinalize`. The event object contains a `canceled` property — if the handler was interrupted or failed to recognize the gesture, `canceled` is set to `true`. Otherwise it is set to `false`. ### onFinalize ```ts -onFinalize: (event: GestureEvent, didSucceed: boolean) => void +onFinalize: (event: GestureEndEvent) => void ``` -Called when handler stops recognizing gestures. If the handler managed to activate, the `didSucceed` argument is set to `true` and `onFinalize` will be called right after `onDeactivate`. Otherwise it is set to `false`. +Called when handler stops recognizing gestures. The event object contains a `canceled` property — if the handler failed to activate or was interrupted, `canceled` is set to `true`. If the handler managed to activate and completed successfully, `canceled` is set to `false` and `onFinalize` will be called right after `onDeactivate`. ### onTouchesDown @@ -107,7 +107,7 @@ Called when there will be no more information about this pointer. It may be call = { handlerTag: number; @@ -115,6 +115,10 @@ export type GestureEvent = { pointerType: PointerType; } & HandlerData; +export type GestureEndEvent = { + canceled: boolean; +} & GestureEvent; + export enum PointerType { TOUCH, STYLUS, @@ -126,6 +130,10 @@ export enum PointerType { `GestureEvent` contains properties common to all gestures (`handlerTag`, `numberOfPointers`, `pointerType`) along with gesture-specific data defined in each gesture's documentation. +### GestureEndEvent + +`GestureEndEvent` extends `GestureEvent` with a `canceled` property. It is used in the [`onDeactivate`](#ondeactivate) and [`onFinalize`](#onfinalize) callbacks. When `canceled` is `true`, the gesture was interrupted or failed to activate. When `false`, the gesture completed successfully. + ### TouchEvent - {`onDeactivate: (event: ${props.gesture}HandlerData, didSucceed: boolean) => void`} + {`onDeactivate: (event: ${props.gesture}HandlerData & { canceled: boolean }) => void`} } -Set the callback that is being called when the gesture that was recognized by the handler finishes. It will be called only if the handler was previously in the active state. +Set the callback that is being called when the gesture that was recognized by the handler finishes. It will be called only if the handler was previously in the active state. The event object contains a `canceled` property — if the gesture was interrupted, `canceled` is set to `true`. Otherwise it is set to `false`. ### onFinalize { - {`onFinalize: (event: ${props.gesture}HandlerData, didSucceed: boolean) => void`} + {`onFinalize: (event: ${props.gesture}HandlerData & { canceled: boolean }) => void`} } -Set the callback that is being called when the handler finalizes handling gesture - the gesture was recognized and has finished or it failed to recognize. +Set the callback that is being called when the handler finalizes handling gesture - the gesture was recognized and has finished or it failed to recognize. The event object contains a `canceled` property — if the gesture failed to activate or was interrupted, `canceled` is set to `true`. Otherwise it is set to `false`. ### onTouchesDown diff --git a/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx b/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx index a5e2fcf253..ea4fb11eb5 100644 --- a/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx +++ b/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx @@ -92,6 +92,32 @@ code2={ `} /> +### canceled instead of didSucceed + +In RNGH2, `onEnd` and `onFinalize` received a second `success` boolean parameter. In RNGH3, this has been replaced with a `canceled` property on the event object itself. Note that the logic is inverted — `canceled: true` corresponds to the old `success: false`. + + { + if (success) { + console.log('Gesture succeeded!'); + } + }); +`} +code2={ +`const gesture = useTapGesture({ + onDeactivate: (event) => { + if (!event.canceled) { + console.log('Gesture succeeded!'); + } + }, +}); +`} +/> + ### onChange `onChange` callback has been removed, and its functionality has been integrated into `onUpdate`. You can now access `change*` properties in `onUpdate` callback. diff --git a/packages/docs-gesture-handler/docs/under-the-hood/state.md b/packages/docs-gesture-handler/docs/under-the-hood/state.md index 01e613028f..4cc8ad7b01 100644 --- a/packages/docs-gesture-handler/docs/under-the-hood/state.md +++ b/packages/docs-gesture-handler/docs/under-the-hood/state.md @@ -14,6 +14,6 @@ States manage the internal recognition process. You can hook into these transiti | **`UNDETERMINED`** | The default initial state of every handler. | — | | **`BEGAN`** | The handler has started receiving touch data but hasn't yet met the activation criteria. | [`onBegin`](/docs/fundamentals/callbacks-events#onbegin) | | **`ACTIVE`** | The gesture is recognized and activation criteria are met. | [`onActivate`](/docs/fundamentals/callbacks-events#onactivate) when it first transitions into the `ACTIVE` state.

[`onUpdate`](/docs/fundamentals/callbacks-events#onupdate) when it has new data about the gesture. | -| **`END`** | The user successfully completed the gesture. | [`onDeactivate`](/docs/fundamentals/callbacks-events#ondeactivate) with `didSucceed` parameter set to `true`.

[`onFinalize`](/docs/fundamentals/callbacks-events#onfinalize) with `didSucceed` parameter set to `true`. | -| **`FAILED`** | The handler failed to recognize the gesture. | [`onDeactivate`](/docs/fundamentals/callbacks-events#ondeactivate) if the gesture was in `ACTIVE` state before. `didSucceed` parameter will be set to `false`

[`onFinalize`](/docs/fundamentals/callbacks-events#onfinalize) with `didSucceed` parameter set to `false`. | -| **`CANCELLED`** | The system interrupted the gesture. | [`onDeactivate`](/docs/fundamentals/callbacks-events#ondeactivate) if the gesture was in `ACTIVE` state before. `didSucceed` parameter will be set to `false`

[`onFinalize`](/docs/fundamentals/callbacks-events#onfinalize) with `didSucceed` parameter set to `false`. | +| **`END`** | The user successfully completed the gesture. | [`onDeactivate`](/docs/fundamentals/callbacks-events#ondeactivate) with `event.canceled` set to `false`.

[`onFinalize`](/docs/fundamentals/callbacks-events#onfinalize) with `event.canceled` set to `false`. | +| **`FAILED`** | The handler failed to recognize the gesture. | [`onDeactivate`](/docs/fundamentals/callbacks-events#ondeactivate) if the gesture was in `ACTIVE` state before, with `event.canceled` set to `true`.

[`onFinalize`](/docs/fundamentals/callbacks-events#onfinalize) with `event.canceled` set to `true`. | +| **`CANCELLED`** | The system interrupted the gesture. | [`onDeactivate`](/docs/fundamentals/callbacks-events#ondeactivate) if the gesture was in `ACTIVE` state before, with `event.canceled` set to `true`.

[`onFinalize`](/docs/fundamentals/callbacks-events#onfinalize) with `event.canceled` set to `true`. | diff --git a/skills/gesture-handler-3-migration/SKILL.md b/skills/gesture-handler-3-migration/SKILL.md index 64306480d6..29bab3f552 100644 --- a/skills/gesture-handler-3-migration/SKILL.md +++ b/skills/gesture-handler-3-migration/SKILL.md @@ -40,6 +40,20 @@ In Gesture Handler 3 some of the callbacks were renamed, namely: - `onEnd` -> `onDeactivate` - `onTouchesCancelled` -> `onTouchesCancel` +The `onDeactivate` and `onFinalize` callbacks no longer receive a second `didSucceed`/`success` boolean parameter. Instead, the event object now contains a `canceled` property. Note that the logic is inverted — `canceled: true` corresponds to the old `success: false`. + +```jsx +// Old (RNGH2) +.onEnd((event, success) => { + if (success) { /* gesture succeeded */ } +}) + +// New (RNGH3) +onDeactivate: (event) => { + if (!event.canceled) { /* gesture succeeded */ } +} +``` + In the hooks API `onChange` is no longer available. Instead the `*change*` properties were moved to the event available inside `onUpdate`. All callbacks of a gesture are now using the same type: From 3195d1e5ce7ee48ee92a3e06bfd3b94916599a37 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Tue, 14 Apr 2026 13:52:27 +0200 Subject: [PATCH 4/6] Review changes --- .../src/v3/hooks/callbacks/eventHandler.ts | 2 +- .../src/v3/hooks/utils/eventHandlersUtils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/hooks/callbacks/eventHandler.ts b/packages/react-native-gesture-handler/src/v3/hooks/callbacks/eventHandler.ts index e146c0a58f..29432be019 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/callbacks/eventHandler.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/callbacks/eventHandler.ts @@ -56,7 +56,7 @@ function handleStateChangeEvent< }; if (oldState === State.ACTIVE) { - fillInDefaultValues?.(event as GestureEvent); + fillInDefaultValues?.(endEvent as GestureEndEvent); runCallback( CALLBACK_TYPE.END, callbacks, diff --git a/packages/react-native-gesture-handler/src/v3/hooks/utils/eventHandlersUtils.ts b/packages/react-native-gesture-handler/src/v3/hooks/utils/eventHandlersUtils.ts index 70eff2cb53..3210881b73 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/utils/eventHandlersUtils.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/utils/eventHandlersUtils.ts @@ -7,7 +7,7 @@ import { GestureTouchEventCallback, UnpackedGestureHandlerEvent, } from '../../types'; -import { GestureEndEventCallback } from '../../types/ConfigTypes'; +import type { GestureEndEventCallback } from '../../types/ConfigTypes'; export function useMemoizedGestureCallbacks< THandlerData, From 7ee20a5e85535394193383d1cce37623a77ab92b Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Wed, 15 Apr 2026 15:47:41 +0200 Subject: [PATCH 5/6] Update packages/docs-gesture-handler/docs/guides/upgrading-to-3.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> --- packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx b/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx index ea4fb11eb5..a7a1aeb6c1 100644 --- a/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx +++ b/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx @@ -92,7 +92,7 @@ code2={ `} /> -### canceled instead of didSucceed +### canceled instead of success In RNGH2, `onEnd` and `onFinalize` received a second `success` boolean parameter. In RNGH3, this has been replaced with a `canceled` property on the event object itself. Note that the logic is inverted — `canceled: true` corresponds to the old `success: false`. From eb1570ceee38ad5b0a66255f4de4c77ca96940d1 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Wed, 15 Apr 2026 16:26:13 +0200 Subject: [PATCH 6/6] Fix message --- .../docs-gesture-handler/docs/fundamentals/callbacks-events.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docs-gesture-handler/docs/fundamentals/callbacks-events.mdx b/packages/docs-gesture-handler/docs/fundamentals/callbacks-events.mdx index cb41739995..172205fb22 100644 --- a/packages/docs-gesture-handler/docs/fundamentals/callbacks-events.mdx +++ b/packages/docs-gesture-handler/docs/fundamentals/callbacks-events.mdx @@ -58,7 +58,7 @@ Called each time a pointer tracked by the gesture changes state, typically due t onDeactivate: (event: GestureEndEvent) => void ``` -Called when handler stops recognizing gestures, but only if the handler activated. It is called before `onFinalize`. The event object contains a `canceled` property — if the handler was interrupted or failed to recognize the gesture, `canceled` is set to `true`. Otherwise it is set to `false`. +Called when handler stops recognizing gestures, but only if the handler activated. It is called before `onFinalize`. The event object contains a `canceled` property — if the gesture was interrupted, `canceled` is set to `true`. Otherwise it is set to `false`. ### onFinalize