Skip to content

Remove begin from StateManager#4024

Open
akwasniewski wants to merge 8 commits intomainfrom
@akwasniewski/remove-begin-from-sm
Open

Remove begin from StateManager#4024
akwasniewski wants to merge 8 commits intomainfrom
@akwasniewski/remove-begin-from-sm

Conversation

@akwasniewski
Copy link
Contributor

Description

We realised that there is no real use case for allowing to state manage gestures which did not receive touches. Thus we decided to remove the function GestureStateManager.begin() altogether, as if gesture had received touches it's state is set to begun. Additonally StateManager will no longer force going through begin state in activate, thus if we attempt to use state manager to activate a non begun gesture it won't activate.

Test plan

Tested on the following example

Details
import { COLORS, commonStyles } from '../common-app/src/common';
import React from 'react';
import { View } from 'react-native';
import {
  GestureHandlerRootView,
  GestureDetector,
  useLongPressGesture,
  GestureStateManager,
  useSimultaneousGestures,
  usePanGesture,
  useManualGesture,
} from 'react-native-gesture-handler';
import Animated, {
  useAnimatedStyle,
  useSharedValue,
  withTiming,
} from 'react-native-reanimated';

export default function TwoPressables() {
  const isActivated = [useSharedValue(0), useSharedValue(0)];

  const lp1 = useLongPressGesture({
    onBegin: () => {
      'worklet';
      console.log("other begin");
    },
    onActivate: () => {
      'worklet';
      isActivated[0].value = 1;
      console.log(`Box 1: long pressed`);
    },
    onFinalize: () => {
      'worklet';
      console.log("other finalized")
      isActivated[0].value = 0;
    },
    disableReanimated: true,
    minDuration: 1000000
  })

  const lp2 = useManualGesture({
    onTouchesDown: () => {
      console.log("touches down")
    },
    onTouchesUp: (e) => {
      console.log("touches up")
      GestureStateManager.deactivate(e.handlerTag)
    },
    onActivate: () => {
      'worklet';
      isActivated[1].value = 1;
      GestureStateManager.activate(lp1.handlerTag);
      console.log(`Box 2: long pressed`);
    },
    onFinalize: () => {
      'worklet';
      console.log("fhduifhuifhuiwefhuf")
      isActivated[1].value = 0;
    },
    disableReanimated: true,
  })

  const pan = usePanGesture({
    onActivate: () => {
      'worklet';
      GestureStateManager.activate(lp2.handlerTag);
    }
  })

  const g2 = useSimultaneousGestures(lp2, pan);

  const gestures = []

  gestures[0] = lp1;
  gestures[1] = g2;


  const colors = [COLORS.PURPLE, COLORS.NAVY, COLORS.GREEN, COLORS.RED];

  function Box({ index }: { index: number }) {
    const animatedStyle = useAnimatedStyle(() => ({
      opacity: isActivated[index].value === 1 ? 0.5 : 1,
      transform: [
        { scale: withTiming(isActivated[index].value === 1 ? 0.95 : 1) },
      ],
    }));

    return (
      <GestureDetector gesture={gestures[index]}>
        <Animated.View
          style={[
            commonStyles.box,
            { backgroundColor: colors[index] },
            animatedStyle,
          ]}
        />
      </GestureDetector>
    );
  }
  return (
    <GestureHandlerRootView>
      <View style={commonStyles.centerView}>
        <Box index={0} />
        <Box index={1} />
      </View>
    </GestureHandlerRootView>
  );
}

Copilot AI review requested due to automatic review settings March 13, 2026 12:20
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes the GestureStateManager.begin() API (v3) and tightens manual activation behavior so gestures cannot be activated unless they have already transitioned into BEGAN (i.e., received touches / began recognition).

Changes:

  • Remove begin() from the v3 GestureStateManager API (native + web implementations).
  • Stop forcing UNDETERMINED -> BEGAN -> ACTIVE in manual activation flows (web/iOS/Android).
  • Prevent GestureHandler.activate() on web from transitioning from UNDETERMINED to ACTIVE.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts Disallows activation unless the handler is already in BEGAN.
packages/react-native-gesture-handler/src/v3/gestureStateManager.web.ts Removes begin() and stops auto-begin in activate() for web v3.
packages/react-native-gesture-handler/src/v3/gestureStateManager.ts Removes begin() from the exported v3 state manager type/impl.
packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm Blocks ACTIVE state changes for handlers that never left UNDETERMINED.
packages/react-native-gesture-handler/apple/RNGestureHandler.h Exposes lastState for the new activation guard; formatting changes.
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt Blocks UNDETERMINED -> ACTIVE and adjusts when handlers get recorded.
Comments suppressed due to low confidence (1)

packages/react-native-gesture-handler/src/v3/gestureStateManager.web.ts:25

  • GestureStateManager.activate always calls GestureHandlerOrchestrator.instance.recordHandlerIfNotPresent(handler) even when handler.activate(true) will no-op (e.g. when the handler is still UNDETERMINED). Since the web orchestrator only cleans up END/FAILED/CANCELLED handlers, this can leave an UNDETERMINED handler permanently recorded, causing an orchestrator list leak and extra processing. Consider checking handler.state === State.BEGAN before recording/activating (or otherwise avoid recording when activation is ignored).
    const handler = NodeManager.getHandler(handlerTag);
    ensureHandlerAttached(handler);

    GestureHandlerOrchestrator.instance.recordHandlerIfNotPresent(handler);
    handler.activate(true);
  },

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@akwasniewski akwasniewski marked this pull request as ready for review March 13, 2026 15:34
@akwasniewski
Copy link
Contributor Author

This PR intentionally breaks current StateManager example, I'm trying to think of a good replacement, if you have any ideas, let me know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants