diff --git a/apps/common-app/src/new_api/components/switchAndInput/index.tsx b/apps/common-app/src/new_api/components/switchAndInput/index.tsx index a54fc8373a..95d86203cd 100644 --- a/apps/common-app/src/new_api/components/switchAndInput/index.tsx +++ b/apps/common-app/src/new_api/components/switchAndInput/index.tsx @@ -6,7 +6,6 @@ import { } from 'react-native-gesture-handler'; import React, { useState } from 'react'; import { StyleSheet, Text, View } from 'react-native'; -import type { NativeGestureEvent } from 'react-native-gesture-handler'; export default function SwitchTextInputExample() { const [switchOn, setSwitchOn] = useState(false); @@ -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/apps/common-app/src/new_api/hover_mouse/hover/index.tsx b/apps/common-app/src/new_api/hover_mouse/hover/index.tsx index 2c6586b289..fb1d589547 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 @@ -30,8 +30,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 74e24caf16..458a069761 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 f4044e6e4d..b6e5a6ed1e 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, diff --git a/packages/docs-gesture-handler/docs/fundamentals/callbacks-events.mdx b/packages/docs-gesture-handler/docs/fundamentals/callbacks-events.mdx index 9dc9d10f33..172205fb22 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 gesture was interrupted, `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..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,6 +92,32 @@ code2={ `} /> +### 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`. + + { + 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/packages/react-native-gesture-handler/src/v3/components/GestureButtons.tsx b/packages/react-native-gesture-handler/src/v3/components/GestureButtons.tsx index 90b508e9ad..8183335631 100644 --- a/packages/react-native-gesture-handler/src/v3/components/GestureButtons.tsx +++ b/packages/react-native-gesture-handler/src/v3/components/GestureButtons.tsx @@ -5,13 +5,14 @@ import type { RawButtonProps, RectButtonProps, } from './GestureButtonsProps'; +import type { GestureEndEvent, GestureEvent } from '../types'; import React, { useRef } from 'react'; -import type { GestureEvent } from '../types'; import GestureHandlerButton from '../../components/GestureHandlerButton'; import type { NativeHandlerData } from '../hooks/gestures/native/NativeTypes'; import createNativeWrapper from '../createNativeWrapper'; type CallbackEventType = GestureEvent; +type EndCallbackEventType = GestureEndEvent; /** * @deprecated `RawButton` is deprecated, use `Clickable` instead @@ -66,23 +67,23 @@ export const BaseButton = (props: BaseButtonProps) => { props.onActivate?.(e); }; - const onDeactivate = (e: CallbackEventType, didSucceed: boolean) => { + const onDeactivate = (e: EndCallbackEventType) => { onActiveStateChange?.(false); - if (didSucceed && !longPressDetected.current) { + if (!e.canceled && !longPressDetected.current) { onPress?.(e.pointerInside); } - props.onDeactivate?.(e, didSucceed); + props.onDeactivate?.(e); }; - const onFinalize = (e: CallbackEventType, didSucceed: boolean) => { + const onFinalize = (e: EndCallbackEventType) => { if (longPressTimeout.current !== undefined) { clearTimeout(longPressTimeout.current); longPressTimeout.current = undefined; } - props.onFinalize?.(e, didSucceed); + props.onFinalize?.(e); }; return ( diff --git a/packages/react-native-gesture-handler/src/v3/components/Pressable.tsx b/packages/react-native-gesture-handler/src/v3/components/Pressable.tsx index a68b883276..187c437cbf 100644 --- a/packages/react-native-gesture-handler/src/v3/components/Pressable.tsx +++ b/packages/react-native-gesture-handler/src/v3/components/Pressable.tsx @@ -273,13 +273,13 @@ const Pressable = (props: PressableProps) => { stateMachine.reset(); handlePressOut(pressableEvent, false); }, - onFinalize: (_event, success) => { + onFinalize: (event) => { if (Platform.OS !== 'web') { return; } stateMachine.handleEvent( - success ? StateMachineEvent.FINALIZE : StateMachineEvent.CANCEL + event.canceled ? StateMachineEvent.CANCEL : StateMachineEvent.FINALIZE ); handleFinalize(); @@ -312,14 +312,14 @@ const Pressable = (props: PressableProps) => { stateMachine.handleEvent(StateMachineEvent.NATIVE_START); } }, - onFinalize: (_event, success) => { + onFinalize: (event) => { // On Web we use LongPress.onFinalize instead of Native.onFinalize, // as Native cancels on mouse move, and LongPress does not. if (Platform.OS === 'web') { return; } stateMachine.handleEvent( - success ? StateMachineEvent.FINALIZE : StateMachineEvent.CANCEL + event.canceled ? StateMachineEvent.CANCEL : StateMachineEvent.FINALIZE ); handleFinalize(); diff --git a/packages/react-native-gesture-handler/src/v3/components/Touchable/Touchable.tsx b/packages/react-native-gesture-handler/src/v3/components/Touchable/Touchable.tsx index 8f4eaaf6b9..9f71ac8105 100644 --- a/packages/react-native-gesture-handler/src/v3/components/Touchable/Touchable.tsx +++ b/packages/react-native-gesture-handler/src/v3/components/Touchable/Touchable.tsx @@ -1,4 +1,8 @@ -import type { CallbackEventType, TouchableProps } from './TouchableProps'; +import type { + CallbackEventType, + EndCallbackEventType, + TouchableProps, +} from './TouchableProps'; import React, { useCallback, useRef } from 'react'; import type { ButtonProps } from '../../../components/GestureHandlerButton'; import GestureHandlerButton from '../../../components/GestureHandlerButton'; @@ -79,10 +83,10 @@ export const Touchable = (props: TouchableProps) => { ); const onDeactivate = useCallback( - (e: CallbackEventType, success: boolean) => { + (e: EndCallbackEventType) => { onActiveStateChange?.(false); - if (success && !longPressDetected.current) { + if (!e.canceled && !longPressDetected.current) { onPress?.(e.pointerInside); } }, @@ -90,7 +94,7 @@ export const Touchable = (props: TouchableProps) => { ); const onFinalize = useCallback( - (e: CallbackEventType) => { + (e: EndCallbackEventType) => { onPressOut?.(e); if (longPressTimeout.current !== undefined) { diff --git a/packages/react-native-gesture-handler/src/v3/components/Touchable/TouchableProps.ts b/packages/react-native-gesture-handler/src/v3/components/Touchable/TouchableProps.ts index 6c71a71a39..eb58ccaa16 100644 --- a/packages/react-native-gesture-handler/src/v3/components/Touchable/TouchableProps.ts +++ b/packages/react-native-gesture-handler/src/v3/components/Touchable/TouchableProps.ts @@ -1,10 +1,11 @@ import type { BaseButtonProps, RawButtonProps } from '../GestureButtonsProps'; +import type { GestureEndEvent, GestureEvent } from '../../types'; import type { ButtonProps } from '../../../components/GestureHandlerButton'; -import type { GestureEvent } from '../../types'; import type { NativeHandlerData } from '../../hooks/gestures/native/NativeTypes'; import type { PressableAndroidRippleConfig as RNPressableAndroidRippleConfig } from 'react-native'; export type CallbackEventType = GestureEvent; +export type EndCallbackEventType = GestureEndEvent; type PressableAndroidRippleConfig = { [K in keyof RNPressableAndroidRippleConfig]?: Exclude< 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 10a9e8fc6d..167cc746d7 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 @@ -1,6 +1,7 @@ import type { ChangeCalculatorType, GestureCallbacks, + GestureEndEvent, GestureEvent, GestureHandlerEventWithHandlerData, GestureStateChangeEventWithHandlerData, @@ -44,25 +45,29 @@ function handleStateChangeEvent< ) { fillInDefaultValues?.(event as GestureEvent); runCallback(CALLBACK_TYPE.START, callbacks, event); - } else if (oldState !== state && state === State.END) { - if (oldState === State.ACTIVE) { - fillInDefaultValues?.(event as GestureEvent); - 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) { - fillInDefaultValues?.(event as GestureEvent); - runCallback(CALLBACK_TYPE.END, callbacks, event, false); + fillInDefaultValues?.(endEvent as GestureEndEvent); + 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 f47c03a553..0000000000 --- a/packages/react-native-gesture-handler/src/v3/hooks/callbacks/stateChangeHandler.ts +++ /dev/null @@ -1,73 +0,0 @@ -import type { - GestureCallbacks, - GestureStateChangeEventWithHandlerData, - StateChangeEventWithHandlerData, -} from '../../types'; -import { - flattenAndFilterEvent, - isEventForHandlerWithTag, - maybeExtractNativeEvent, - runCallback, -} from '../utils'; -import { CALLBACK_TYPE } from '../../../handlers/gestures/gesture'; -import type { ReanimatedContext } from '../../../handlers/gestures/reanimatedWrapper'; -import { State } from '../../../State'; - -export function getStateChangeHandler< - THandlerData, - TExtendedHandlerData extends THandlerData, ->( - 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 - ) { - // If the native recognizer skipped the BEGAN state, we still need to call the callback - if (oldState === State.UNDETERMINED) { - runCallback(CALLBACK_TYPE.BEGAN, callbacks, event); - } - - 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 e0af9be510..0000000000 --- a/packages/react-native-gesture-handler/src/v3/hooks/callbacks/touchEventHandler.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { GestureCallbacks, TouchEvent } from '../../types'; -import { - isEventForHandlerWithTag, - maybeExtractNativeEvent, - runCallback, - touchEventTypeToCallbackType, -} from '../utils'; -import type { GestureTouchEvent } from '../../../handlers/gestureHandlerCommon'; -import { TouchEventType } from '../../../TouchEventType'; - -export function getTouchEventHandler< - THandlerData, - TExtendedHandlerData extends THandlerData, ->( - 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 788cb080ff..0000000000 --- a/packages/react-native-gesture-handler/src/v3/hooks/callbacks/updateHandler.ts +++ /dev/null @@ -1,56 +0,0 @@ -import type { - ChangeCalculatorType, - GestureCallbacks, - GestureUpdateEventWithHandlerData, - UpdateEventWithHandlerData, -} from '../../types'; -import { - flattenAndFilterEvent, - isEventForHandlerWithTag, - maybeExtractNativeEvent, - runCallback, -} from '../utils'; -import { CALLBACK_TYPE } from '../../../handlers/gestures/gesture'; -import type { ReanimatedContext } from '../../../handlers/gestures/reanimatedWrapper'; -import { tagMessage } from '../../../utils'; - -export function getUpdateHandler< - THandlerData, - TExtendedHandlerData extends THandlerData, ->( - 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 d194aa488c..7270edcd74 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 @@ -1,11 +1,11 @@ import type { GestureCallbacks, GestureEventCallback, - GestureEventCallbackWithDidSucceed, GestureTouchEventCallback, UnpackedGestureHandlerEvent, } from '../../types'; import { CALLBACK_TYPE } from '../../../handlers/gestures/gesture'; +import type { GestureEndEventCallback } from '../../types/ConfigTypes'; import { TouchEventType } from '../../../TouchEventType'; import { useMemo } from 'react'; @@ -55,8 +55,8 @@ function getHandler( ): | GestureEventCallback | GestureEventCallback - | GestureEventCallbackWithDidSucceed - | GestureEventCallbackWithDidSucceed + | GestureEndEventCallback + | GestureEndEventCallback | GestureTouchEventCallback | undefined { 'worklet'; @@ -100,7 +100,6 @@ export function touchEventTypeToCallbackType( } type SingleParameterCallback = (event: T) => void; -type DoubleParameterCallback = (event: T, didSucceed: boolean) => void; export function runCallback< THandlerData, @@ -108,8 +107,7 @@ export function runCallback< >( type: CALLBACK_TYPE, callbacks: GestureCallbacks, - event: UnpackedGestureHandlerEvent, - didSucceed?: boolean + event: UnpackedGestureHandlerEvent ) { 'worklet'; const handler = getHandler(type, callbacks); @@ -118,9 +116,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 a4d8bfda92..ddbcd728d9 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 type { import type { AnimatedEvent, ChangeCalculatorType, + GestureEndEvent, GestureEvent, } from './EventTypes'; import type { 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; @@ -34,10 +34,8 @@ export type GestureCallbacks< | GestureEventCallback | AnimatedEvent | undefined; - onDeactivate?: - | GestureEventCallbackWithDidSucceed - | undefined; - onFinalize?: GestureEventCallbackWithDidSucceed | undefined; + onDeactivate?: GestureEndEventCallback | undefined; + onFinalize?: GestureEndEventCallback | undefined; onTouchesDown?: GestureTouchEventCallback | undefined; onTouchesMove?: GestureTouchEventCallback | undefined; onTouchesUp?: GestureTouchEventCallback | undefined; 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 18427b91ae..542a456c0c 100644 --- a/packages/react-native-gesture-handler/src/v3/types/EventTypes.ts +++ b/packages/react-native-gesture-handler/src/v3/types/EventTypes.ts @@ -59,6 +59,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, 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: