From 1e24ffab9db2da4a9597962318b40b7dd55ba397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Fran=C3=A7on?= Date: Sat, 15 Aug 2026 20:30:20 +0200 Subject: [PATCH 1/2] Pass `reanimatedEventHandler` only when the Reanimated detector is used The handler is built whenever `disableReanimated` is unset, but `shouldUseReanimatedDetector` additionally requires worklet callbacks. A gesture with none renders the plain host component, which forwards props verbatim, so it received Reanimated's event handler object under `onGestureHandlerReanimatedEvent`, a codegen DirectEventHandler prop. React then throws out of `getListener` instead of dispatching. --- .../src/v3/detectors/NativeDetector.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/detectors/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/detectors/NativeDetector.tsx index 7672bd9509..816c6631f1 100644 --- a/packages/react-native-gesture-handler/src/v3/detectors/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/detectors/NativeDetector.tsx @@ -62,8 +62,14 @@ export function NativeDetector< gesture.detectorCallbacks.reanimatedEventHandler, } : { - onGestureHandlerReanimatedEvent: - gesture.detectorCallbacks.reanimatedEventHandler, + // `reanimatedEventHandler` is built whenever `disableReanimated` is unset, but + // `shouldUseReanimatedDetector` additionally requires worklet callbacks. When it is + // false we render the plain host component, which forwards props verbatim, so passing + // the handler would put a non-function on a codegen `DirectEventHandler` prop. + onGestureHandlerReanimatedEvent: gesture.config + .shouldUseReanimatedDetector + ? gesture.detectorCallbacks.reanimatedEventHandler + : undefined, }; return ( From d849577763a90e61aadf380b8b51985899563006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Fran=C3=A7on?= Date: Mon, 17 Aug 2026 12:21:32 +0200 Subject: [PATCH 2/2] Apply the same guard on the web branch Web is safe today because the handler only emits Reanimated events when `forReanimated` is set, which itself comes from `dispatchesReanimatedEvents` and so already requires `shouldUseReanimatedDetector`. Hoisting the guard keeps both branches in sync rather than relying on that. --- .../src/v3/detectors/NativeDetector.tsx | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/detectors/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/detectors/NativeDetector.tsx index 816c6631f1..c299bb6e10 100644 --- a/packages/react-native-gesture-handler/src/v3/detectors/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/detectors/NativeDetector.tsx @@ -51,25 +51,24 @@ export function NativeDetector< // On native, Reanimated handles routing internally based on the event names // passed to the useEvent hook. We only need to pass it once, so that Reanimated // can setup its internal listeners. + // + // `reanimatedEventHandler` is built whenever `disableReanimated` is unset, but + // `shouldUseReanimatedDetector` additionally requires worklet callbacks. When it is false we + // render the plain host component, which forwards props verbatim, so passing the handler would + // put a non-function on a codegen `DirectEventHandler` prop. + const reanimatedEventHandler = gesture.config.shouldUseReanimatedDetector + ? gesture.detectorCallbacks.reanimatedEventHandler + : undefined; + const reanimatedHandlers = Platform.OS === 'web' ? { - onGestureHandlerReanimatedEvent: - gesture.detectorCallbacks.reanimatedEventHandler, - onGestureHandlerReanimatedStateChange: - gesture.detectorCallbacks.reanimatedEventHandler, - onGestureHandlerReanimatedTouchEvent: - gesture.detectorCallbacks.reanimatedEventHandler, + onGestureHandlerReanimatedEvent: reanimatedEventHandler, + onGestureHandlerReanimatedStateChange: reanimatedEventHandler, + onGestureHandlerReanimatedTouchEvent: reanimatedEventHandler, } : { - // `reanimatedEventHandler` is built whenever `disableReanimated` is unset, but - // `shouldUseReanimatedDetector` additionally requires worklet callbacks. When it is - // false we render the plain host component, which forwards props verbatim, so passing - // the handler would put a non-function on a codegen `DirectEventHandler` prop. - onGestureHandlerReanimatedEvent: gesture.config - .shouldUseReanimatedDetector - ? gesture.detectorCallbacks.reanimatedEventHandler - : undefined, + onGestureHandlerReanimatedEvent: reanimatedEventHandler, }; return (