30
<TouchableOpacity style={{ flex: 1 }} >
  <ImageBackground
    source={require('../../images/home.jpg')}>
      <View style={styles.item} collapsable={false}>
        <H3>{contentData[i].name}</H3>
        <Text>{contentData[i].description}</Text>
      </View>
  </ImageBackground>
</TouchableOpacity>

I have a list of TouchableOpacity inside a ScrollView. I want to disable highlighting effect of TouchableOpacity. When scrolling I want to highlight only when onPress event is triggered. Because it may confuse the user that it is pressed.

Pritish Vaidya
  • 21,561
  • 3
  • 58
  • 76
Henok Tesfaye
  • 8,287
  • 13
  • 47
  • 84

8 Answers8

56

Simply pass activeOpactity prop with value 1.

<TouchableOpacity activeOpacity={1}>....</TouchableOpacity>

Make sure you import TouchableOpacity from "react-native" not from "react-native-gesture-handler".

Jeevan Prakash
  • 686
  • 5
  • 7
  • 1
    I'm confused--why use TouchableOpacity if you set the element's opacity to 1 when the user interacts with it? Isn't the whole goal to indicate user interactions by changing the opacity? – duhaime Mar 09 '20 at 16:39
  • @duhaime honestly speaking the default effect looks so weird sometimes that developer wants to get rid of it entirely. At the end, there will be UX loss either way. Sometimes, removing that effect seems better option. – Jeevan Prakash Mar 22 '20 at 17:42
  • 3
    @JeevanPrakash then you should use TouchableWithoutFeedback – Will Apr 24 '20 at 00:43
16

Try setting the activeOpacity prop on the TouchableOpacity to 1 when scrolling. Use default settings when the user stops scrolling.

https://facebook.github.io/react-native/docs/touchableopacity#activeopacity

codehesh
  • 875
  • 10
  • 29
11

You can try changing param delayPressIn. Look doc.

<TouchableOpacity delayPressIn={150} > 
 {children}
</TouchableOpacity>
Andrey Patseiko
  • 3,687
  • 1
  • 25
  • 24
5

You could try replace TouchOpacity with RectButton in 'react-native-gesture-handler'. And don't forget to replace the ScrollView import from 'react-native' to 'react-native-gesture-handler'.

I found this solution in here.

It just said:

provides native and platform default interaction for buttons that are placed in a scrollable container (in which case the interaction is slightly delayed to prevent button from highlighting when you fling)

Prateek Gupta
  • 2,422
  • 2
  • 16
  • 30
wscheng
  • 51
  • 1
  • 1
  • 1
    The documentation link has updated. It's now here: https://docs.swmansion.com/react-native-gesture-handler/docs/api/components/buttons/ – JCraine Jun 28 '21 at 02:56
4

You can make use of onScrollBeginDrag and onScrollEndDrag props.

 state = {
    scrollBegin: false
  }

  scrollStart = () => this.setState({scrollBegin: true})   
  scrollEnd = () => this.setState({scrollBegin: false})

 <ScrollView onScrollBeginDrag={this.scrollStart} onScrollEndDrag={this.scrollEnd}>
   ... Other stuff
 </ScrollView>

and set activeOpacity={1} for TouchableOpacity when this.state.scrollBegin=true

Pritish Vaidya
  • 21,561
  • 3
  • 58
  • 76
  • Thank you @Pritish Vaidya but it doesn't solve my problem. Your solution is worked after scrolling is started but not the moment i started scroll. – Henok Tesfaye Sep 20 '18 at 09:51
2

We implemeted a custom Touchable component using TouchableOpacity as click element and a wrapper View handling the opacity of the children elements.

By setting activeOpacity={1} to default and the pressed state to true when clicking, we can delay the rest of the onPress functionality by a unnoticeable 100ms to display an opacity shift when clicking. Which is shipped to the wrapper View. The View is wrapped inside the touchable instead of outside to better preserve styling.

We also added cleanup when component is unmounted in useEffect()

import React, { useEffect, useState } from "react";
import { View, TouchableOpacity } from "react-native";


const Touchable = (props) => {

  const { children, onPress } = props;

  const [pressed, setPressed] = useState(false);

  useEffect(() => {
    return setPressed(false);
  }, []);

  return (
      <TouchableOpacity
        {...props}
        activeOpacity={1}
        onPress={() => {
          setPressed(true);
          setTimeout(() => {
            setPressed(false);
            onPress();
          }, 100);
        }}
      >
        <View style={{opacity: pressed ? 0.8 : 1}}>
        {children}
        </View>
      </TouchableOpacity>

  );
};
export default Touchable;
1

I had the same issue, so I wrote this class that I use instead of <TouchableOpacity> in my code:

import React, { Component } from 'react';
import { TouchableOpacity } from 'react-native';
import TimerMixin from 'react-timer-mixin';

class TouchableOpacityScrollable extends Component {
  _onPress() {
    const { onPress } = this.props;

    // Looking in the TouchableOpacity source code we see that
    // the touch Opacity is 150, and that it comes back in 250 milliseconds.
    // @see https://github.com/facebook/react-native/blob/c416b40542ece64e26fb2298485ae42eeebc352a/Libraries/Components/Touchable/TouchableOpacity.js
    this.refs.touchableOpacity.setOpacityTo(0.2, 150);

    TimerMixin.setTimeout(() => {
      onPress();
      this.refs.touchableOpacity.setOpacityTo(1, 250);
    }, 150);
  }

  render() {
    const { style, children } = this.props;

    return (
      <TouchableOpacity
        ref="touchableOpacity"
        style={style}
        activeOpacity={1.0}
        onPress={() => this._onPress()}
      >
        {children}
      </TouchableOpacity>
    );
  }
}

export default TouchableOpacityScrollable;

You will have to install react-timer-mixin to prevent possible crashes.

Enjoy!

Francois Nadeau
  • 7,023
  • 2
  • 49
  • 58
0

after upgrading RN version to 0.63.2 TouchableOpacity is working like it should, during scrolling, hover effect doesn't appears