Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Native TouchableOpacity not updating styles

I have a strange issue with the TouchableOpacity component. I have a MainButton component which takes 2 props, item and disabled. Based on the the disabled prop I want my MainButton component to apply a different style. The problem is when the TouchableOpacity component re renders, it does not refresh the style. The prop disabled is correctly set on re render.

What makes this strange is that if i change it to a TouchableHeighlight it works as expected.

Does anyone know why? Is this a bug in TouchableOpacity?

import React, { Component } from 'react'
import UI from '../styles/ui'

import {
  Text,
  TouchableOpacity
} from 'react-native'

const ui = new UI()
const styles = ui.styles

class MainButton extends Component {
  constructor (props) {
    super(props)
    this.state = {
      disabled : props.disabled,
      item: props.item
    }
  }

  componentWillReceiveProps(props) {
    this.setState({disabled:props.disabled})
  }

  render() {
    var normalStyles = [styles.mainButton,styles.widthEighty]
    var disabledStyle = [styles.mainButton,styles.widthEighty,styles.lowOpacity]
    var correctStyles = this.state.disabled ? disabledStyle : normalStyles
    console.log(this.state.disabled,'this.state.disabled ? ');
    return (
      <TouchableOpacity disabled={this.state.disabled} style={correctStyles} accessibilityLabel={this.state.item.name} onPress={this.state.item.onPress.bind(this)}>
        <Text style={styles.mediumLabel}>{this.state.item.name}</Text>
      </TouchableOpacity>
    );
  }
}

export { MainButton as default }
like image 491
Zolve Avatar asked Aug 28 '17 13:08

Zolve


3 Answers

First, I don't think pass the disabled and item props to state. You can directly do it in this way and get rid of constructor as well as componentWillReceiveProps. componentWillReceiveProps lifecycle method will be deprecated soon, so they don't encourage its usage.

Also if possible, separate event handler from item props. It's not best way to do that

render () {
  const { disabled, item, onPress } = this.props;
  const { name } = item;
  ...
  return (
    ...
    <TouchableOpacity
      disabled={disabled}
      style={disabled ? disabledStyle : normalStyle}
      accessibilityLabel={name}
      onPress={onPress}
    >
      <Text style={styles.mediumLabel}>{name}</Text>
    </TouchableOpacity>
    ...
  ); 
}
like image 146
Daniel Zheng Avatar answered Oct 21 '22 19:10

Daniel Zheng


The workaround I came up with is to use

setOpacityTo(value) in order to update the canvas.

I did this componentDidUpdate() as this lifecycle method get's called whenever your new style has been applied within your component. So I gave the <TouchableOpacity/> component a ref and updated it whenever it's style did change.

Example:

import React, { Component } from 'react'
import UI from '../styles/ui'

import {
  Text,
  TouchableOpacity
} from 'react-native'

const ui = new UI()
const styles = ui.styles

class MainButton extends Component {
  constructor (props) {
    super(props)
    this.state = {
      disabled : props.disabled,
      item: props.item
    }
  }

  componentWillReceiveProps(props) {
    this.setState({disabled:props.disabled})
  }

  componentDidUpdate() {
    this.refs['touchable'].setOpacityTo(1.0);
  }

  render() {
    var normalStyles = [styles.mainButton,styles.widthEighty]
    var disabledStyle = [styles.mainButton,styles.widthEighty,styles.lowOpacity]
    var correctStyles = this.state.disabled ? disabledStyle : normalStyles
    console.log(this.state.disabled,'this.state.disabled ? ');
    return (
      <TouchableOpacity ref={'touchable'} disabled={this.state.disabled} style={correctStyles} accessibilityLabel={this.state.item.name} onPress={this.state.item.onPress.bind(this)}>
        <Text style={styles.mediumLabel}>{this.state.item.name}</Text>
      </TouchableOpacity>
    );
  }
}

export { MainButton as default }
like image 1
YoshiJaeger Avatar answered Oct 21 '22 20:10

YoshiJaeger


If you use opacity style (which using by TouchableOpacity), you should set this style for children of TouchableOpacity (ex a View) like this:

<TouchableOpacity
  disabled={disabled}
  accessibilityLabel={name}
  onPress={onPress}
>
  <View style={disabled ? disabledStyle : normalStyle}>
    <Text style={styles.mediumLabel}>{name}</Text>
  </View>
</TouchableOpacity>
like image 1
Robin Huy Avatar answered Oct 21 '22 21:10

Robin Huy