react-native-windows icon indicating copy to clipboard operation
react-native-windows copied to clipboard

PanResponder / Responder don't appear to work on New Architecture

Open austinhallock opened this issue 1 year ago • 2 comments

Problem Description

Hey folks! Have been playing around with 0.76.0 and loving it so far. One thing that doesn't seem to work, which I haven't found an existing GitHub issue for is PanResponder. I'm trying out the basic example from https://reactnative.dev/docs/panresponder and it doesn't move.

If I tweak the responder a bit to:

  const panResponder = useRef(
    PanResponder.create({
      onStartShouldSetPanResponder: () => true,
      onMoveShouldSetPanResponder: () => true,
      onPanResponderGrant: () => {
        console.log('onPanResponderGrant');
      },
      onPanResponderMove: (event, gesture) => {
        console.log(gesture);
      },
      onPanResponderRelease: () => {
        console.log('onPanResponderRelease');
        pan.extractOffset();
      },
    }),
  ).current;

It'll log the onPanResponderGrant (unless the view has a <Pressable /> as a child) and onPanResponderRelease, but it never triggers onPanResponderMove

The same can be said for Gesture Responder and onResponderMove (which seems like it should work).

onPointerMove does work, so we can probably use that in the interim :)

Steps To Reproduce

Fresh install of new architecture -> copy and paste example from https://reactnative.dev/docs/panresponder

Expected Results

No response

CLI version

15.0.0

Environment

Windows 11 10.0.22631
React Native 0.76.1
RNW 0.76.0

Community Modules

No response

Target Platform Version

None

Target Device(s)

No response

Visual Studio Version

None

Build Configuration

None

Snack, code example, screenshot, or link to a repository

No response

austinhallock avatar Nov 15 '24 23:11 austinhallock

So glad you're trying out 0.76. Thanks for the feedback! Our best guess here is that the touch events are not quite hooked up right. We'll investigate.

chrisglein avatar Nov 21 '24 19:11 chrisglein

It seems I have the same issue on React Native 0.81.5 with Expo 54.

Steffi3rd avatar Nov 24 '25 17:11 Steffi3rd

I also got this issue with react native 0.83.0

I looked into PanResponder code within react native and it seems like the touchHistory timestamps are only set once therefore difference in movements can't be calculated. This is workaround I came up with (someone can extract this into function if they wish).

This workaround saves reference to PanResponders inner state and uses its update functions for movement, adds multiple checks so panResponder state should stay valid (ehhh I hope so), and finally override function call to View so we can inject our own callback for movement.

function ExampleWithPanResponder()
{
    const [gestureY, setGestureY] = useState(0);
    const [gestureX, setGestureX] = useState(0);
    const gestureState = useRef<PanResponderGestureState>({});

    const updateTimeStamps = (event) => {
          let currentTimestamp = performance.now() * 100;
          const previousTimestamp = ev.touchHistory.mostRecentTimeStamp;
          if(ev.touchHistory.mostRecentTimeStamp === currentTimestamp) {
            // Potential fail safe so pan responder should always work
            currentTimestamp += 1;
          } else
            ev.touchHistory.mostRecentTimeStamp = currentTimestamp;
          ev.touchHistory.touchBank.forEach((touchRecord: any) => {
            touchRecord.currentTimeStamp = ev.touchHistory.mostRecentTimeStamp;
            touchRecord.previousTimeStamp = previousTimestamp;
          });
    };
    const panResponder = useRef(
        PanResponder.create({
            onStartShouldSetPanResponder: () => true,
            onMoveShouldSetPanResponder: () => true,
            onPanResponderTerminationRequest: () => false,
            onShouldBlockNativeResponder: () => true,
            onPanResponderGrant: (_ev, state) => {
               updateTimeStamps(ev);
              gestureState.current = state;
            },
            onPanResponderRelease: (ev, state) => {
              updateTimeStamps(ev);
            },
        })
    ).current;

        return (
        <View 
          style={{width: '100%', height: '100%', position: 'relative'}}
            {...panResponder.panHandlers}
            onResponderMove={(ev: GestureResponderEvent) => {
                updateTimeStamps(ev);

                PanResponder._updateGestureStateOnMove(gestureState.current, ev.touchHistory);
                setGestureX(gestureState.current.moveX);
                setGestureY(gestureState.current.moveY);
            }}
            >
              <View style=
                {{
                  height: 300,
                  width: 300,
                  transform: [
                    { translateX: gestureX },
                    { translateY: gestureY }
                  ],
                  backgroundColor: 'lightblue',
                  position: 'absolute',
                }}
              />
        </View>);
}

And this is the result:

Gif showing an example of how PanResponder can be setup so it drags element on the screen

Edit:

Found the root issue. Seems like within touch history api there's use of Date.now() instead of performance.now() -> Date.now() can evaluate into same value between two frames and movement assumes to be always different. Edited code so it matches with performance.

SturdyPose avatar Dec 18 '25 19:12 SturdyPose