Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -22,8 +21,8 @@ export default function SwitchTextInputExample() {
<TextInput
onBegin={() => 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..."
Expand All @@ -50,9 +49,7 @@ export default function SwitchTextInputExample() {
<View style={styles.switchRow}>
<Switch
onBegin={() => 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);
Expand Down
4 changes: 2 additions & 2 deletions apps/common-app/src/new_api/hover_mouse/hover/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
16 changes: 8 additions & 8 deletions apps/common-app/src/new_api/showcase/overlap/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
Expand All @@ -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');
}
Expand Down Expand Up @@ -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');
}
Expand All @@ -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');
}
Expand Down
4 changes: 2 additions & 2 deletions apps/common-app/src/new_api/simple/longPress/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,18 @@ Called each time a pointer tracked by the gesture changes state, typically due t
### onDeactivate

```ts
onDeactivate: (event: GestureEvent<HandlerData>, didSucceed: boolean) => void
onDeactivate: (event: GestureEndEvent<HandlerData>) => 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<HandlerData>, didSucceed: boolean) => void
onFinalize: (event: GestureEndEvent<HandlerData>) => 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

Expand Down Expand Up @@ -107,14 +107,18 @@ Called when there will be no more information about this pointer. It may be call
<CollapsibleCode
label="Show composed types definitions"
expandedLabel="Hide composed types definitions"
lineBounds={[0, 4]}
lineBounds={[0, 8]}
src={`
export type GestureEvent<HandlerData> = {
handlerTag: number;
numberOfPointers: number;
pointerType: PointerType;
} & HandlerData;

export type GestureEndEvent<HandlerData> = {
canceled: boolean;
} & GestureEvent<HandlerData>;

export enum PointerType {
TOUCH,
STYLUS,
Expand All @@ -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

<CollapsibleCode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,21 @@ Set the callback that is being called when the gesture is recognized by the hand

{
<CodeBlock className="language-ts">
{`onDeactivate: (event: ${props.gesture}HandlerData, didSucceed: boolean) => void`}
{`onDeactivate: (event: ${props.gesture}HandlerData & { canceled: boolean }) => void`}
</CodeBlock>
}

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

{
<CodeBlock className="language-ts">
{`onFinalize: (event: ${props.gesture}HandlerData, didSucceed: boolean) => void`}
{`onFinalize: (event: ${props.gesture}HandlerData & { canceled: boolean }) => void`}
</CodeBlock>
}

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

Expand Down
26 changes: 26 additions & 0 deletions packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

<CodeComparison
label1={"RNGH 2"}
label2={"RNGH 3"}
code1={
`const gesture = Gesture.Tap()
.onEnd((event, success) => {
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.
Expand Down
6 changes: 3 additions & 3 deletions packages/docs-gesture-handler/docs/under-the-hood/state.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. <br /><br /> [`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`. <br/><br/> [`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` <br/><br/> [`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` <br/><br/> [`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`. <br/><br/> [`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`. <br/><br/> [`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`. <br/><br/> [`onFinalize`](/docs/fundamentals/callbacks-events#onfinalize) with `event.canceled` set to `true`. |
Original file line number Diff line number Diff line change
Expand Up @@ -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<NativeHandlerData>;
type EndCallbackEventType = GestureEndEvent<NativeHandlerData>;

/**
* @deprecated `RawButton` is deprecated, use `Clickable` instead
Expand Down Expand Up @@ -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 (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -79,18 +83,18 @@ 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);
}
},
[onActiveStateChange, onPress]
);

const onFinalize = useCallback(
(e: CallbackEventType) => {
(e: EndCallbackEventType) => {
onPressOut?.(e);

if (longPressTimeout.current !== undefined) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<NativeHandlerData>;
export type EndCallbackEventType = GestureEndEvent<NativeHandlerData>;

type PressableAndroidRippleConfig = {
[K in keyof RNPressableAndroidRippleConfig]?: Exclude<
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type {
ChangeCalculatorType,
GestureCallbacks,
GestureEndEvent,
GestureEvent,
GestureHandlerEventWithHandlerData,
GestureStateChangeEventWithHandlerData,
Expand Down Expand Up @@ -44,25 +45,29 @@ function handleStateChangeEvent<
) {
fillInDefaultValues?.(event as GestureEvent<TExtendedHandlerData>);
runCallback(CALLBACK_TYPE.START, callbacks, event);
} else if (oldState !== state && state === State.END) {
if (oldState === State.ACTIVE) {
fillInDefaultValues?.(event as GestureEvent<TExtendedHandlerData>);
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<THandlerData> = {
...event,
canceled,
};

if (oldState === State.ACTIVE) {
fillInDefaultValues?.(event as GestureEvent<TExtendedHandlerData>);
runCallback(CALLBACK_TYPE.END, callbacks, event, false);
fillInDefaultValues?.(endEvent as GestureEndEvent<TExtendedHandlerData>);
runCallback<THandlerData, TExtendedHandlerData>(
CALLBACK_TYPE.END,
callbacks,
endEvent
);
Comment thread
j-piasecki marked this conversation as resolved.
}
runCallback(CALLBACK_TYPE.FINALIZE, callbacks, event, false);
runCallback<THandlerData, TExtendedHandlerData>(
CALLBACK_TYPE.FINALIZE,
callbacks,
endEvent
);

if (context) {
context.lastUpdateEvent = undefined;
Expand Down
Loading
Loading