callstack / react-native-pager-view

React Native wrapper for the Android ViewPager and iOS UIPageViewController.
MIT License
2.68k stars 411 forks source link

Random onPress handle in FlatList items while swiping pages #424

Open a-sane opened 3 years ago

a-sane commented 3 years ago

Environment

expo v42.0.0

"react-native-pager-view": "5.4.0"

Description

Random onPress handle in FlatList items while swiping pages Especially noticeable on low-end android devices and in apps with many heavy components

Reproducible Demo

There is a function in demo that imitate CPU intensive operation in order for the issue to be stably reproduced in simple demo on high-end devices

https://snack.expo.dev/@sane.ecg/pager-onpress-issue

https://user-images.githubusercontent.com/764967/128613740-e384306f-a100-4b7c-9ade-7db12683d6e5.mp4

kyoz commented 3 years ago

Face the same issue with ios + hermes on.

kyoz commented 3 years ago

For more information

In the past, i'v use "@react-native-community/viewpager": "5.0.11" and it work perfectly on android.

But currently when try ios build, this problem occured.

I'v also update to "react-native-pager-view": "5.4.0" but this still persist.

The only solution that work for me now is listen for onPageScrollStateChanged event, store the scroll state and only trigger onPress on other event if scrollState is idle

MichaelDanielTom commented 3 years ago

Depending on the type of touchable component you're using, you should be able to use the react-native-gesture-handler version to get the desired behavior. These components are more tightly integrated with the native gesture system(s), and while I'm not entirely sure what changes were made to react-native-pager-view from the old repo, it seems like it uses the react-native gesture responder system less.

P.S. While the react-native-gesture-handler docs say that TouchableWithoutFeedback and the like are drop-in replacements for the regular React-Native components, there are a few annoying differences regarding layout like this.

brsaylor2 commented 2 years ago

I have this problem on Android, and ignoring onPress if scrollState !== 'idle' helps (thank you, @kyoz). Unfortunately, onPageScrollStateChanged does not fire 100% of the time. I have two nested PagerView components, and the the inner one's onPageScrollStateChanged is unreliable when the outer one has recently scrolled. If I ignore onPress until the outer PagerView has been idle for a period of time, the problem is less likely to occur.

alantoa commented 2 years ago

I usereact-native-tab-view v3 is depend on this, same issue! I hope solve this problem.

I guess it's because of the async between the JavaScript thread and Main Thread. If you use v2 (use react-native-reanimateed builded), It won't happen.

salonisahu commented 2 years ago

Any update?

Gregoirevda commented 2 years ago

@grabbou Could we get an update on this please?

Gregoirevda commented 2 years ago

Found a solution! Replace <TouchableHighlight with <FixedTouchableHighlight in your FlatList

// FixedTouchableHighlight.js
import React, { useRef } from 'react';
import { TouchableHighlight } from 'react-native';

export default function FixedTouchableHighlight({
  onPress,
  onPressIn,
  ...props
}) {
  const _touchActivatePositionRef = useRef(null);

  function _onPressIn(e) {
    const { pageX, pageY } = e.nativeEvent;

    _touchActivatePositionRef.current = {
      pageX,
      pageY,
    };

    onPressIn?.(e);
  }

  function _onPress(e) {
    const { pageX, pageY } = e.nativeEvent;

    const absX = Math.abs(_touchActivatePositionRef.current.pageX - pageX);
    const absY = Math.abs(_touchActivatePositionRef.current.pageY - pageY);

    const dragged = absX > 2 || absY > 2;
    if (!dragged) {
      onPress?.(e);
    }
  }

  return (
    <TouchableHighlight onPressIn={_onPressIn} onPress={_onPress} {...props}>
      {props.children}
    </TouchableHighlight>
  );
}
LeathanTeal commented 2 years ago

@Gregoirevda Thank you for the workaround. Hopefully this is a temporary fix and a future update will prevent touchable from activating during screen transitions.

likeSo commented 2 years ago

I have the same problem on React Navigation v6

batuhansahan commented 2 years ago

Still an issue +1

kashmiry commented 1 year ago

Found a solution! Replace <TouchableHighlight with <FixedTouchableHighlight in your FlatList

// FixedTouchableHighlight.js
import React, { useRef } from 'react';
import { TouchableHighlight } from 'react-native';

export default function FixedTouchableHighlight({
  onPress,
  onPressIn,
  ...props
}) {
  const _touchActivatePositionRef = useRef(null);

  function _onPressIn(e) {
    const { pageX, pageY } = e.nativeEvent;

    _touchActivatePositionRef.current = {
      pageX,
      pageY,
    };

    onPressIn?.(e);
  }

  function _onPress(e) {
    const { pageX, pageY } = e.nativeEvent;

    const absX = Math.abs(_touchActivatePositionRef.current.pageX - pageX);
    const absY = Math.abs(_touchActivatePositionRef.current.pageY - pageY);

    const dragged = absX > 2 || absY > 2;
    if (!dragged) {
      onPress?.(e);
    }
  }

  return (
    <TouchableHighlight onPressIn={_onPressIn} onPress={_onPress} {...props}>
      {props.children}
    </TouchableHighlight>
  );
}

Thank you @Gregoirevda for providing a temporary solution 🙏 it works! I even used it as The issue is still present, currently on:

"react-native-pager-view": "^6.2.0",
"react-native-reanimated": "^2.10.0",
"react-native": "0.70.7",
"react-native-gesture-handler": "^2.6.0",
Fire4FoxtroT commented 11 months ago

I just patched it react-native-tab-view/src/PanResponderAdapter.tsx

const panResponder = PanResponder.create({ onMoveShouldSetPanResponder: canMoveScreen,

// my change instead canMoveScreen 
onMoveShouldSetPanResponderCapture: () => swipeEnabled,

onPanResponderGrant: startGesture,
onPanResponderMove: respondToGesture,
onPanResponderTerminate: finishGesture,
onPanResponderRelease: finishGesture,
onPanResponderTerminationRequest: () => true,

});

I hope it helps someone I applied the solution from https://github.com/facebook/react-native/issues/27355#issuecomment-981082420