Pass `reanimatedEventHandler` only when the Reanimated detector is used (#4429)
## Description
`NativeDetector` always passes `reanimatedEventHandler` to the native
component, even when the gesture does not use the Reanimated detector.
Two different conditions decide whether the handler exists and whether
the Reanimated detector is used.
`useGestureCallbacks` builds the handler whenever `disableReanimated` is
unset:
```ts
// src/v3/hooks/useGestureCallbacks.ts
let reanimatedEventHandler;
if (!config.disableReanimated) {
```
`shouldUseReanimatedDetector` additionally requires worklet callbacks:
```ts
// src/v3/hooks/utils/configUtils.ts
config.shouldUseReanimatedDetector =
!config.disableReanimated &&
Reanimated !== undefined &&
hasWorkletEventHandlers(config) &&
!config.dispatchesAnimatedEvents;
```
So a gesture with no worklet callbacks gets `shouldUseReanimatedDetector
=== false`, renders the plain `HostGestureDetector`, which forwards
props verbatim, and receives Reanimated's `{ workletEventHandler }`
object under `onGestureHandlerReanimatedEvent`. That prop is a codegen
`DirectEventHandler`, so React throws out of `getListener` instead of
dispatching:
```
Expected `onGestureHandlerReanimatedEvent` listener to be a function, instead got a value of `object` type.
```
It stays latent most of the time, because a handler on a plain detector
gets `dispatchesReanimatedEvents: shouldUseReanimatedDetector &&
!runOnJS`, which is false, so it never emits the event. It surfaces once
a handler ends up attached to a detector that is not its own. The prop
is invalid either way.
This passes the handler only when the Reanimated detector is actually
used. The event prop is part of the static view config, so `undefined`
only means there is no JS listener, and native still emits the event.
I left the web branch untouched, since I have not tested whether the
same mismatch applies there. Happy to extend it if you think it does.
Fixes #4428
## Test plan
- `yarn ts-check`, `yarn lint:js` and `yarn test` in
`packages/react-native-gesture-handler`.
- Repro app:
https://github.com/antFrancon/rngh-reanimated-handler-repro. It renders
four gestures and prints, for each, which detector `NativeDetector`
picks and the `typeof` of the handler it receives.
- Before: `useNativeGesture()`, `usePanGesture()` and `useTapGesture({
onActivate })` all render `HostGestureDetector` with `typeof
reanimatedEventHandler === 'object'`.
- After: those three receive `undefined`, and the worklet control still
gets the handler on `ReanimatedNativeDetector`.
- We have been running this change as a patch in an app on Expo SDK 57,
RN 0.86.2, React 19.2.3, Reanimated 4.5.3, Fabric, Hermes, iOS, with no
regressions in gesture behaviour. A
antFrancon committed
91ccdd004f0225fa017c1fc91e1f3971042042f9
Parent: b2d23f1
Committed by GitHub <noreply@github.com>
on 8/18/2026, 8:58:21 AM