dozo
dozo

Reputation: 339

How to make the images a button?

I am working on a react native app where I am trying to make some images act like a button so that when you press on them they print a statement in the console.

The images are displayed like this: enter image description here

The code I have is:

class ImageList extends Component {
componentWillMount(){
const ds = new ListView.DataSource({
  rowHasChanged: (r1, r2) => r1 !== r2
});
this.dataSource = ds.cloneWithRows(this.props.images);
}

imageTouched(){
 console.log('pressed');
}

renderRow(rowData){
 const {uri} = rowData;

 return(
<View style={{padding: 1, alignSelf: 'flex-start'}}>
  <TouchableHighlight onPress={this.imageTouched}>
   <Image style={styles.imageSize} source={{uri: uri}} />
  </TouchableHighlight>
</View>


 )
}

  render() {
    return (
      <View>
        <ListView
          contentContainerStyle={styles.list}
          dataSource={this.dataSource}
          renderRow={this.renderRow}
        />
      </View>
    );
  }
}

var styles = StyleSheet.create({
  imageSize: {

//newWidth is the width of the device divided by 4. 
//This is so that four images will display in each row. 
    width: newWidth,
    height: newWidth,
    padding: 2
  },
  list: {
        flexDirection: 'row',
        flexWrap: 'wrap'
    }
});

When I run it there are no errors but when I touch the images nothing happens. I have checked the console but nothing is printed.

How do I get each image to act as a button?

Upvotes: 0

Views: 2551

Answers (3)

benwixen
benwixen

Reputation: 992

Like others have mentioned, the problem is that this is not bound in the renderRow()-method. I think the easiest way to fix this is to change renderRow() to be an arrow-function:

renderRow = (rowData) => {
  const {uri} = rowData;
  return (
    <View style={{padding: 1, alignSelf: 'flex-start'}}>
      <TouchableHighlight onPress={this.imageTouched}>
        <Image style={styles.imageSize} source={{uri: uri}} />
      </TouchableHighlight>
    </View>
  );
}

Arrow function always have this set to their containing scope when invoked, so now this.imageTouched will resolve.

Notice that you don't have to do anything with your imageTouched()-function or invocation, since it's not referencing this.

PS. This syntax depends on Public Class Fields, which is a Stage 2 proposal of the language standard at the time of writing (likely to be included, already in use in internal React-code). This feature is possible to use with a babel-plugin that is enabled by default in React Native projects.

PS2. Note that declaring the method with an arrow function instead of using an arrow function in the invocation will create one instance of the method per component instance, instead of one instance per render. This should really be fine performance-wise.

Upvotes: 2

vinayr
vinayr

Reputation: 11244

Try this

<TouchableHighlight onPress={() => this.imageTouched()}>

Make sure to bind renderRow

renderRow={this.renderRow.bind(this)}

Upvotes: 0

jevakallio
jevakallio

Reputation: 35970

React components defined as ES6 classes do not autobind methods' this context to the component instance. In this particular case, the renderRow callback is not bound and the context refers to the global application context instead, so the reference to this.imageTouched is undefined.

A common pattern you see a lot is to bind the callbacks in your render method:

<ListView
  renderRow={this.renderRow.bind(this)}
/>

This, however, has the effect of creating a new instance of the function on every render, which causes unnecessary work for the garbage collector.

Another alternative is to use (lexically scoped) arrow functions and call the methods explicitly:

<ListView
  renderRow={(data) => this.renderRow(data);
/>

But this has the same unwanted effect of unnecessarily creating functions on every render.

A slightly more verbose, but more "correct" way is to bind the callbacks in the class constructor:

constructor(props) {
  super(props);
  this.renderRow = this.renderRow.bind(this);
}

Upvotes: 0

Related Questions