11

I have a React Native child component, which renders a button in a semi-transparent state if the disabled prop is set to true. The prop is likely to be updated after the app initially loads (once it has got its data), so will not be the initial state of the component.

I can see that once I interact with the button it changes its state, but for some reason not before. I can see, both from the logs and from the onPress behaviour, that the prop is updating. I've tried different approaches but none seemed to fix the issue.

class TestButton extends React.Component {

  constructor(props) {
    super(props);
  }

  render() {
    const buttonOpacity = (this.props.disabled  ? disabledOpacity : 1.0);
    console.log ("test disabled", this.props.disabled, buttonOpacity);

    return (
      <BubbleText style={{opacity: buttonOpacity}} onPress={
        () => ! this.props.disabled && doSomething() }>
          { this.props.testNumber }
      </BubbleText>
    );
  }
}
Adamski
  • 3,307
  • 2
  • 29
  • 72

5 Answers5

10

There does seem to be an issue with setting the opacity of TouchableOpacity buttons. I'm using react-native@0.55.4. If the opacity is set and then updated the new render does not seem to change the opacity value even though it is being passed to the component style.

There is a native way to do this with TouchableOpacity. This also benefits from disabling all press events if using the disabled prop.

<TouchableOpacity
    disabled={ this.props.is_disabled }
    activeOpacity={ this.props.is_disabled ? .6 : 1 }>
    <Text>Custom Button</Text>
</TouchableOpacity>

One caveat to the above, setting the activeOpacity does not appear to change the text opacity only the backgroundColor.

Alternatively using rgba values to specify the opacity does work.

export class CustomButton extends Component {

    get_button_style() {
        let _button_style = [this.props.button_style]

        if (this.props.is_disabled) {
            _button_style.push({
                backgroundColor: 'rgba(0, 0, 0, .6')
            });
        }

        return _button_style;
    }

    render() {
        return(
            <TouchableOpacity
                style= { this.get_button_style() }>
                <Text> Custom Button </Text>
            </TouchableOpacity>
        )
    }
}
kgstew
  • 393
  • 2
  • 12
  • 1
    Another option is to wrap the TouchableOpacity in a view. Then set the opacity in the view instead. – rolznz Feb 22 '19 at 06:44
1

Seems like a known issue https://github.com/facebook/react-native/issues/17105

One workaround is to wrap your TouchableOpacity's content in a view and apply the opacity styling to that view instead of directly to Touchable opacity.

Pnar Sbi Wer
  • 458
  • 6
  • 8
  • Wow using RN 0.60.4 nowadays and this bug still occurs. This workaround works but feels dirty! thanks. – msqar Aug 06 '19 at 20:54
0

It's hard to say just from the snippet, it's possible that the problem is in the parent component using this one. Adding the code for that might help identify what the problem is.

Sorry dont have enough rep to add a comment.

Nemi Shah
  • 766
  • 1
  • 6
  • 11
0

The underlying component was a TouchableOpacity. It seems there is an issue with setting its opacity externally. I solved the issue in this case by defining colours explicitly, not using opacity:

class TestButton extends React.Component {

  constructor(props) {
    super(props);
  }

  render() {
      return (
        <BubbleText fill={this.props.disabled ? disabledFill : undefined} textStyle={this.props.disabled ? {color: disabledText} : {}} onPress={
          () => ! this.props.disabled && loadTest(this.props.navigator, this.props.set + this.props.testNumber, this.props.children)
          }>
            { this.props.testNumber }
          </BubbleText>
          );

  }
}

In another part of my code, I added a conditional to render a component as a View with opacity if disabled, and a TouchableOpacity if not.

Adamski
  • 3,307
  • 2
  • 29
  • 72
0

Use TouchableOpacity from react-native-gesture-handler, it has a prop called containerStyle, your TouchableOpacity will automatically update opacity when "this.props.is_disabled" be false or true. Without it, you will need to restart application to show opacity:

<TouchableOpacity onPress={() => {}} 
                    disabled={this.props.is_disabled} 
                    containerStyle={{
                        opacity: this.props.is_disabled ? 1 : .4,
                    }}
                    style={}>
                </TouchableOpacity>
Otavio
  • 1
  • 1