Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Random onPress handle in FlatList items while swiping pages #424

Open
a-sane opened this issue Aug 7, 2021 · 13 comments
Open

Random onPress handle in FlatList items while swiping pages #424

a-sane opened this issue Aug 7, 2021 · 13 comments

Comments

@a-sane
Copy link

a-sane commented Aug 7, 2021

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

video_2021-08-08_00-58-55.mp4
@a-sane a-sane changed the title Random onPress handle in FlatList items while swiping tabs Random onPress handle in FlatList items while swiping pages Aug 7, 2021
@kyoz
Copy link

kyoz commented Aug 13, 2021

Face the same issue with ios + hermes on.

@kyoz
Copy link

kyoz commented Aug 14, 2021

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
Copy link

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
Copy link

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
Copy link

alantoa commented Dec 17, 2021

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
Copy link

Any update?

@Gregoirevda
Copy link

@grabbou Could we get an update on this please?

@Gregoirevda
Copy link

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
Copy link

@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
Copy link

likeSo commented Mar 9, 2022

I have the same problem on React Navigation v6

@batuhansahan
Copy link

Still an issue +1

@kashmiry
Copy link

kashmiry commented Apr 4, 2023

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
Copy link

Fire4FoxtroT commented Oct 23, 2023

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 facebook/react-native#27355 (comment)

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

No branches or pull requests