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

When using Pressable inside a horizontal carousel, the scroll is registered as a tap #150

Closed
diego-paired opened this issue Mar 26, 2022 · 17 comments
Assignees
Labels
bug Something isn't working

Comments

@diego-paired
Copy link

    "react-native-reanimated": "2.2.4",
    "react-native-reanimated-carousel": "^2.3.2",
    "react-native-gesture-handler": "^2.3.2",

I'm using the Carousel in its default mode horizontally scrolling.
When the items rendered use Pressable and the user scrolls horizontally, the scroll happens but a press is also registered when lifting the finger.
If I swap Pressable for a Gesture Handler Touchable, the error disappears.

Please advise, thank you.

@diego-paired diego-paired added the bug Something isn't working label Mar 26, 2022
@dohooo
Copy link
Owner

dohooo commented Mar 28, 2022

    "react-native-reanimated": "2.2.4",
    "react-native-reanimated-carousel": "^2.3.2",
    "react-native-gesture-handler": "^2.3.2",

I'm using the Carousel in its default mode horizontally scrolling.
When the items rendered use Pressable and the user scrolls horizontally, the scroll happens but a press is also registered when lifting the finger.
If I swap Pressable for a Gesture Handler Touchable, the error disappears.

Please advise, thank you.

Can you show me a video, I can't imagine what it looks like

@diego-paired
Copy link
Author

recording.mp4

When pressing to scroll sideways, the pressable is also triggered and navigates to the content of the card

@dohooo
Copy link
Owner

dohooo commented Mar 28, 2022

hmm... show me your code?

@dohooo
Copy link
Owner

dohooo commented Mar 28, 2022

Does that happen if you run my demo code(In homepage)?

@jean-sebb
Copy link

jean-sebb commented Mar 30, 2022

Hi @diego-paired and @dohooo,
To give maybe a hint, In my case, I have the same problem only with Android when the carousel is under a modal, otherwise the carousel works fine.
The problem appear when the carousel is under a modal with a touchable opacity (from react-native library or gesture handler is the same), but i can't scroll the carousel in my case, the onPress action trigger immediately .

The Component under modal

`import React, { useRef } from 'react';
import { Image, View, TouchableOpacity, Text, Dimensions } from "react-native";
import Carousel from 'react-native-reanimated-carousel';
import { colors } from "../utils/const";
import { isIphoneX } from '../utils/isIphoneX';
import { FoodTruckItem } from './Home/FoodTruckItemComponent';

const manPicture = require('../assets/man.png')

export function OrderCancelComponent({ carouselItems, openMenu, closeModal }) {

    const carouseOrderCancellRef = useRef();

    return (
        <View style={{ flex: 1, height: '100%', width: '100%', alignItems: 'center', backgroundColor: 'white' }}>
            <View style={{ height: '60%', width: '80%', justifyContent: 'flex-end' }}>
                <Image style={{ height: '40%', width: '100%', resizeMode: 'contain' }} source={manPicture}></Image>
                <Text style={{ fontSize: 24, fontWeight: 'bold', fontFamily: 'Poppins', color: colors.blueText, width: '100%', textAlign: 'center', marginTop: 20 }}>Oups! Ce véhicule n’est pas disponible</Text>
                <Text style={{ fontSize: 18, fontWeight: '500', fontFamily: 'Poppins', color: colors.blueText, width: '100%', textAlign: 'center', marginTop: 20 }}>Pas de panique, d’autres t’attendent, jette un coup d’oeil à nos suggestions 👇</Text>
            </View>
            <View style={{ flex: 1, paddingBottom: isIphoneX() ? 20 : 0, height: '40%', width: '100%', justifyContent: 'flex-end', alignItems: 'center' }}>
                <Text style={{ fontSize: 16, fontWeight: 'bold', fontFamily: 'Poppins', color: 'black', width: '90%', textAlign: 'left' }}>Nos suggestions :</Text>
                <View style={{ height: 120, width: '100%', justifyContent: 'center', alignItems: 'center' }}>
                    <Carousel
                        ref={carouseOrderCancellRef}
                        width={Dimensions.get('screen').width}
                        height={120}
                        data={carouselItems}
                        renderItem={({ item }) => <FoodTruckItem item={item} openMenuTapped={() => openMenu(item, false)}></FoodTruckItem>}
                        // onSnapToItem={(index) => setMarkerSelected(index)}
                        // onScrollEnd={(prev, current) => setMarkerSelected(current)}
                        loop={true}
                        mode={"parallax"}
                        modeConfig={{ parallaxScrollingScale: 1, parallaxScrollingOffset: 60 }}
                    />
                </View>
                <TouchableOpacity onPress={closeModal} style={{ height: 52, width: '90%', marginTop: 15, marginBottom: 15, justifyContent: 'center', alignItems: 'center', backgroundColor: colors.orange, borderRadius: 10}}>
                    <Text style={{ fontSize: 16, fontWeight: 'bold', color: 'white' }}>Voir la carte</Text>
                </TouchableOpacity>
            </View>
        </View>
    );
}`

The carousel item component

`import React from 'react';
import { Image, View, Text, Dimensions, TouchableOpacity } from "react-native";
import { colors } from "../../utils/const";
import { sharedStyles } from '../../utils/styles';

const halalIcon = require('../../assets/home/halalFood.png')
const screenWidth = Dimensions.get('screen').width

export function FoodTruckItem({ item, openMenuTapped }) {
    return (
        <View style={{ height: 120, width: screenWidth * 0.9, justifyContent: 'center', alignItems: 'center' }}>
            <TouchableOpacity activeOpacity={1} onPress={openMenuTapped} style={{ ...sharedStyles.shadow, shadowRadius: 5, height: 110, width: '90%', borderRadius: 10, backgroundColor: 'white' }}>
                <View style={{ height: '100%', width: '100%', flexDirection: 'row' }}>
                    <View style={{ height: '100%', width: '30%', justifyContent: 'center', alignItems: 'center' }}>
                        <Image source={{ uri: item.truckDetails.logo }} style={{ height: '80%', width: '80%', resizeMode: 'contain' }}></Image>
                    </View>
                    <View style={{ height: '100%', width: '70%' }}>
                        <View style={{ height: '60%', width: '100%', justifyContent: 'center', alignItems: 'flex-start' }}>
                            {item.truckDetails.halal &&
                                < Image source={halalIcon} style={{ flex: 1, position: 'absolute', top: 5, right: 10, resizeMode: 'contain', height: 40, width: 40 }}></Image>
                            }
                            <Text style={{ fontSize: 18, fontWeight: 'bold', color: 'black', width: '70%' }}>{item.truckDetails.name} •</Text>
                            <Text style={{ fontSize: 16, fontWeight: 'normal', color: 'black', width: '80%' }}>{item.truckDetails.short_description}</Text>
                        </View>
                        <View style={{ height: '40%', width: '100%', justifyContent: 'center', alignItems: 'flex-start' }}>
                            <View>
                                <Text style={{ fontSize: 16, fontWeight: 'normal', color: colors.orange, textDecorationLine: 'underline' }}>Voir le menu</Text>
                            </View>
                        </View>
                    </View>
                </View>
            </TouchableOpacity>
        </View >
    );
}`

My package json

`{
  "name": "foodrivrepo",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "ios": "react-native run-ios",
    "start": "react-native start",
    "test": "jest",
    "lint": "eslint ."
  },
  "dependencies": {
    "@gorhom/bottom-sheet": "^4.1.5",
    "@react-native-async-storage/async-storage": "^1.17.0",
    "@react-native-firebase/analytics": "^14.7.0",
    "@react-native-firebase/app": "^14.7.0",
    "@react-native-firebase/auth": "^14.7.0",
    "@react-native-firebase/database": "^14.7.0",
    "@react-native-firebase/firestore": "^14.7.0",
    "@react-native-firebase/functions": "^14.7.0",
    "@react-native-firebase/messaging": "^14.7.0",
    "@react-native-masked-view/masked-view": "^0.2.6",
    "@react-navigation/drawer": "^6.3.3",
    "@react-navigation/elements": "^1.3.1",
    "@react-navigation/native": "^6.0.8",
    "@react-navigation/stack": "^6.1.1",
    "axios": "^0.26.1",
    "react": "17.0.2",
    "react-native": "0.67.4",
    "react-native-anchor-carousel": "^4.0.1",
    "react-native-date-picker": "^4.2.0",
    "react-native-fast-image": "^8.5.11",
    "react-native-geolocation-service": "^5.3.0-beta.4",
    "react-native-gesture-handler": "2.3.2",
    "react-native-google-places-autocomplete": "^2.4.1",
    "react-native-image-pan-zoom": "^2.1.12",
    "react-native-image-viewing": "^0.2.1",
    "react-native-image-zoom-viewer": "^3.0.1",
    "react-native-keyboard-aware-scroll-view": "^0.9.5",
    "react-native-maps": "0.30.1",
    "react-native-modal": "^13.0.1",
    "react-native-permissions": "^3.3.1",
    "react-native-phone-number-input": "^2.1.0",
    "react-native-reanimated": "^2.5.0",
    "react-native-reanimated-carousel": "^2.3.2",
    "react-native-safe-area-context": "^4.2.4",
    "react-native-screens": "^3.13.1",
    "react-native-snap-carousel": "^3.9.1"
  },
  "devDependencies": {
    "@babel/core": "^7.17.8",
    "@babel/runtime": "^7.17.8",
    "@react-native-community/eslint-config": "^3.0.1",
    "babel-jest": "^27.5.1",
    "eslint": "8.12.0",
    "jest": "^27.5.1",
    "metro-react-native-babel-preset": "^0.70.0",
    "react-test-renderer": "17.0.2"
  },
  "jest": {
    "preset": "react-native"
  }
}

I hope this will help you !

@dohooo
Copy link
Owner

dohooo commented Mar 30, 2022

Hi @diego-paired and @dohooo, To give maybe a hint, In my case, I have the same problem only with Android when the carousel is under a modal, otherwise the carousel works fine. The problem appear when the carousel is under a modal with a touchable opacity (from react-native library or gesture handler is the same), but i can't scroll the carousel in my case, the onPress action trigger immediately .

The Component under modal

`import React, { useRef } from 'react'; import { Image, View, TouchableOpacity, Text, Dimensions } from "react-native"; import Carousel from 'react-native-reanimated-carousel'; import { colors } from "../utils/const"; import { isIphoneX } from '../utils/isIphoneX'; import { FoodTruckItem } from './Home/FoodTruckItemComponent';

const manPicture = require('../assets/man.png')

export function OrderCancelComponent({ carouselItems, openMenu, closeModal }) {

const carouseOrderCancellRef = useRef();

return (
    <View style={{ flex: 1, height: '100%', width: '100%', alignItems: 'center', backgroundColor: 'white' }}>
        <View style={{ height: '60%', width: '80%', justifyContent: 'flex-end' }}>
            <Image style={{ height: '40%', width: '100%', resizeMode: 'contain' }} source={manPicture}></Image>
            <Text style={{ fontSize: 24, fontWeight: 'bold', fontFamily: 'Poppins', color: colors.blueText, width: '100%', textAlign: 'center', marginTop: 20 }}>Oups! Ce véhicule n’est pas disponible</Text>
            <Text style={{ fontSize: 18, fontWeight: '500', fontFamily: 'Poppins', color: colors.blueText, width: '100%', textAlign: 'center', marginTop: 20 }}>Pas de panique, d’autres t’attendent, jette un coup d’oeil à nos suggestions 👇</Text>
        </View>
        <View style={{ flex: 1, paddingBottom: isIphoneX() ? 20 : 0, height: '40%', width: '100%', justifyContent: 'flex-end', alignItems: 'center' }}>
            <Text style={{ fontSize: 16, fontWeight: 'bold', fontFamily: 'Poppins', color: 'black', width: '90%', textAlign: 'left' }}>Nos suggestions :</Text>
            <View style={{ height: 120, width: '100%', justifyContent: 'center', alignItems: 'center' }}>
                <Carousel
                    ref={carouseOrderCancellRef}
                    width={Dimensions.get('screen').width}
                    height={120}
                    data={carouselItems}
                    renderItem={({ item }) => <FoodTruckItem item={item} openMenuTapped={() => openMenu(item, false)}></FoodTruckItem>}
                    // onSnapToItem={(index) => setMarkerSelected(index)}
                    // onScrollEnd={(prev, current) => setMarkerSelected(current)}
                    loop={true}
                    mode={"parallax"}
                    modeConfig={{ parallaxScrollingScale: 1, parallaxScrollingOffset: 60 }}
                />
            </View>
            <TouchableOpacity onPress={closeModal} style={{ height: 52, width: '90%', marginTop: 15, marginBottom: 15, justifyContent: 'center', alignItems: 'center', backgroundColor: colors.orange, borderRadius: 10}}>
                <Text style={{ fontSize: 16, fontWeight: 'bold', color: 'white' }}>Voir la carte</Text>
            </TouchableOpacity>
        </View>
    </View>
);

}`

The carousel item component

`import React from 'react'; import { Image, View, Text, Dimensions, TouchableOpacity } from "react-native"; import { colors } from "../../utils/const"; import { sharedStyles } from '../../utils/styles';

const halalIcon = require('../../assets/home/halalFood.png') const screenWidth = Dimensions.get('screen').width

export function FoodTruckItem({ item, openMenuTapped }) { return ( <View style={{ height: 120, width: screenWidth * 0.9, justifyContent: 'center', alignItems: 'center' }}> <TouchableOpacity activeOpacity={1} onPress={openMenuTapped} style={{ ...sharedStyles.shadow, shadowRadius: 5, height: 110, width: '90%', borderRadius: 10, backgroundColor: 'white' }}> <View style={{ height: '100%', width: '100%', flexDirection: 'row' }}> <View style={{ height: '100%', width: '30%', justifyContent: 'center', alignItems: 'center' }}> <Image source={{ uri: item.truckDetails.logo }} style={{ height: '80%', width: '80%', resizeMode: 'contain' }}> <View style={{ height: '100%', width: '70%' }}> <View style={{ height: '60%', width: '100%', justifyContent: 'center', alignItems: 'flex-start' }}> {item.truckDetails.halal && < Image source={halalIcon} style={{ flex: 1, position: 'absolute', top: 5, right: 10, resizeMode: 'contain', height: 40, width: 40 }}> } <Text style={{ fontSize: 18, fontWeight: 'bold', color: 'black', width: '70%' }}>{item.truckDetails.name} • <Text style={{ fontSize: 16, fontWeight: 'normal', color: 'black', width: '80%' }}>{item.truckDetails.short_description} <View style={{ height: '40%', width: '100%', justifyContent: 'center', alignItems: 'flex-start' }}> <Text style={{ fontSize: 16, fontWeight: 'normal', color: colors.orange, textDecorationLine: 'underline' }}>Voir le menu ); }`

My package json

{ "name": "foodrivrepo", "version": "0.0.1", "private": true, "scripts": { "android": "react-native run-android", "ios": "react-native run-ios", "start": "react-native start", "test": "jest", "lint": "eslint ." }, "dependencies": { "@gorhom/bottom-sheet": "^4.1.5", "@react-native-async-storage/async-storage": "^1.17.0", "@react-native-firebase/analytics": "^14.7.0", "@react-native-firebase/app": "^14.7.0", "@react-native-firebase/auth": "^14.7.0", "@react-native-firebase/database": "^14.7.0", "@react-native-firebase/firestore": "^14.7.0", "@react-native-firebase/functions": "^14.7.0", "@react-native-firebase/messaging": "^14.7.0", "@react-native-masked-view/masked-view": "^0.2.6", "@react-navigation/drawer": "^6.3.3", "@react-navigation/elements": "^1.3.1", "@react-navigation/native": "^6.0.8", "@react-navigation/stack": "^6.1.1", "axios": "^0.26.1", "react": "17.0.2", "react-native": "0.67.4", "react-native-anchor-carousel": "^4.0.1", "react-native-date-picker": "^4.2.0", "react-native-fast-image": "^8.5.11", "react-native-geolocation-service": "^5.3.0-beta.4", "react-native-gesture-handler": "2.3.2", "react-native-google-places-autocomplete": "^2.4.1", "react-native-image-pan-zoom": "^2.1.12", "react-native-image-viewing": "^0.2.1", "react-native-image-zoom-viewer": "^3.0.1", "react-native-keyboard-aware-scroll-view": "^0.9.5", "react-native-maps": "0.30.1", "react-native-modal": "^13.0.1", "react-native-permissions": "^3.3.1", "react-native-phone-number-input": "^2.1.0", "react-native-reanimated": "^2.5.0", "react-native-reanimated-carousel": "^2.3.2", "react-native-safe-area-context": "^4.2.4", "react-native-screens": "^3.13.1", "react-native-snap-carousel": "^3.9.1" }, "devDependencies": { "@babel/core": "^7.17.8", "@babel/runtime": "^7.17.8", "@react-native-community/eslint-config": "^3.0.1", "babel-jest": "^27.5.1", "eslint": "8.12.0", "jest": "^27.5.1", "metro-react-native-babel-preset": "^0.70.0", "react-test-renderer": "17.0.2" }, "jest": { "preset": "react-native" } }

I hope this will help you !

Could you give me an REPO or EXPO link? It'll make it easier for me to deal with your problem.

@jean-sebb
Copy link

jean-sebb commented Apr 1, 2022

Hi again, sorry for the late answer i found the solution !

In my case it's because of the modal, explanation just below:

`Usage with modals on Android#
On Android RNGH does not work by default because modals are not located under React Native Root view in native hierarchy. In order to make it workable, components need to be wrapped with gestureHandlerRootHOC (it's no-op on iOS and web).

E.g.

const ExampleWithHoc = gestureHandlerRootHOC(function GestureExample() {
  return (
    <View>
      <DraggableBox />
    </View>
  );
});

export default function Example() {
  return (
    <Modal animationType="slide" transparent={false}>
      <ExampleWithHoc />
    </Modal>
  );
}`

Hope this will help someone maybe !
Link to react native gesture handler doc : https://docs.swmansion.com/react-native-gesture-handler/docs/installation/#usage-with-modals-on-android

@jean-sebb
Copy link

jean-sebb commented Apr 1, 2022

currently experiencing the same. just migrated from react-native-snap-carousel, but am facing this issue that i just can't figure out how to fix. i also get a warning that my react-native-gesture-handler is using an old api version, which may have something to do with it. i'm using a bare react-native app, tested on both Android and iOS, with Pressable and TouchOpacity, and both trigger the onPress function when scrolling. I'm not using the modal component, by the way

Maybe try to add gestureHandlerRootHOC on your component and use TouchOpacity from react-native-gesture-handler inside your carousel item.

About the warning i don't know exactly what is it, but after some research it seems to be nothing important. In my case i remove the warning message by downgrading to "react-native-gesture-handler": "2.1.1". but keep in mind that this is only a warning.

If you use react navigation library, it's maybe because of @react-navigation/stack using old api version. Now they use native stack @react-navigation/native-stack

@dohooo
Copy link
Owner

dohooo commented Apr 1, 2022

@diego-paired @halaxysounds

I have reproduced your problem. I used the same version. @diego-paired

"react-native-reanimated": "2.2.4",
"react-native-reanimated-carousel": "^2.3.2",
"react-native-gesture-handler": "^2.3.2",
// will be trigger the onPress function when scrolling
import { TouchableOpacity } from 'react-native';

// will be fine
import { TouchableOpacity } from 'react-native-gesture-handler';

Can you give it a try and let me know the result? I believe this is solvable, because most people haven't talked to me about this kind of problem, and they've obviously used the solution here.

@dohooo
Copy link
Owner

dohooo commented Apr 1, 2022

When I changed to TouchableOpacity of RNGH , the error no longer appeared.

rn.mov

@lehresman
Copy link

Is there an answer for this that does not use TouchableOpacity from react-native-gesture-handler? It is not fully compatible with React Native's TouchableOpacity and will not work in my use case.

Does react-native-reanimated-carousel not work with TouchableOpacity components nested inside?

@dohooo
Copy link
Owner

dohooo commented Oct 20, 2022

Is there an answer for this that does not use TouchableOpacity from react-native-gesture-handler? It is not fully compatible with React Native's TouchableOpacity and will not work in my use case.

Does react-native-reanimated-carousel not work with TouchableOpacity components nested inside?

What's your mean? If you have any other questions. I suggest you create a new issue.

@lehresman
Copy link

The TouchableOpacity component provided by react-native-gesture-handler is not a drop-in replacement for React Native's TouchableOpacity component. It behaves differently and handles styling differently. I'm unfortunately not able to use their TouchableOpacity component as was suggested in this ticket. But I'm still having the same problem as the original poster in this ticket. That is, when I have a component inside of a carousel, swiping left/right is also registered as a tap event by TouchableOpacity, erroniously. Is there any other way around this issue?

@dohooo
Copy link
Owner

dohooo commented Oct 21, 2022

The TouchableOpacity component provided by react-native-gesture-handler is not a drop-in replacement for React Native's TouchableOpacity component. It behaves differently and handles styling differently. I'm unfortunately not able to use their TouchableOpacity component as was suggested in this ticket. But I'm still having the same problem as the original poster in this ticket. That is, when I have a component inside of a carousel, swiping left/right is also registered as a tap event by TouchableOpacity, erroniously. Is there any other way around this issue?

Sry I don't know. Maybe you can create a discussion for this question.

@dohooo
Copy link
Owner

dohooo commented Oct 21, 2022

The TouchableOpacity component provided by react-native-gesture-handler is not a drop-in replacement for React Native's TouchableOpacity component. It behaves differently and handles styling differently. I'm unfortunately not able to use their TouchableOpacity component as was suggested in this ticket. But I'm still having the same problem as the original poster in this ticket. That is, when I have a component inside of a carousel, swiping left/right is also registered as a tap event by TouchableOpacity, erroniously. Is there any other way around this issue?

Infact I don’t think it related to this library, and I think you can find some inspiration from documentation in react-native-gesture-handler library.

@lucianobracco-geojam
Copy link

Hey there, thanks for this amazing library. I am still facing this issue

@silverit
Copy link

silverit commented May 8, 2024

@diego-paired @halaxysounds

I have reproduced your problem. I used the same version. @diego-paired

"react-native-reanimated": "2.2.4",
"react-native-reanimated-carousel": "^2.3.2",
"react-native-gesture-handler": "^2.3.2",
// will be trigger the onPress function when scrolling
import { TouchableOpacity } from 'react-native';

// will be fine
import { TouchableOpacity } from 'react-native-gesture-handler';

Can you give it a try and let me know the result? I believe this is solvable, because most people haven't talked to me about this kind of problem, and they've obviously used the solution here.

Thank you! You save my life 😆

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

6 participants