I'm having trouble to run a function for the onPress event inside a row of a ListView
. I'm following the React Native tutorial trying to continue from there. It seems it's using ES6 syntax style.
This is the relevant part of the code.
/**
* Sample React Native App
* https://github.com/facebook/react-native
*/
import React, {
TouchableHighlight,
AppRegistry,
Component,
Image,
ListView,
StyleSheet,
Text,
View,
Alert,
} from 'react-native';
class AwesomeProject extends Component {
constructor(props) {
super(props);
this.something = this.something.bind(this); // <-- Trying this, not even sure why
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
loaded: false,
};
}
//
//Irrelevant code here. Fetching stuff and renderLoadingView
//
something = function(){
console.log('something');
Alert.alert(
'Alert Title',
'alertMessage',
);
}
render() {
console.log('this', this); //this is an instance of AwesomeProject
if (!this.state.loaded) {
return this.renderLoadingView();
}
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderMovie}
style={styles.listView}
/>
);
}
renderMovie(movie) {
console.log('Not this', this); //this is not an instance of AwesomeProject
return (
<TouchableHighlight onPress={() => {console.log(this); this.something()}}>
<View style={styles.container}>
<Image
source={{uri: movie.posters.thumbnail}}
style={styles.thumbnail}
/>
<View style={styles.rightContainer}>
<Text style={styles.title}
>{movie.title}</Text>
<Text style={styles.year}>{movie.year}</Text>
</View>
</View>
</TouchableHighlight>
);
}
}
//
//More irrelevant code here. Styles and the
//
AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);
I'm unable to run the something
function. I've tried various syntax without success. The problem seems to be that this
is not what I expect it to be, and it doesn't have the variable something
defined.
The only way I had to make it work was to declare an external variable with var something = function(){...}
outside the AwesomeProject class.
Is there a way to access something
while being declared inside AwesomeProject
? Is this maybe a bad practice according to flux architecture or something?
Turns out I found the solution myself, here: https://github.com/goatslacker/alt/issues/283#issuecomment-115391700
The problem is that with ES6 syntax you have no autobinding of this
, so you need to do it yourself.
I've learned a bit more since I posted this answer. The real problem is that
this
is "bound" when the function is executed, not when it's declared. It points to the object the function is attached to when called. For example:obj.foo(); //inside foo, this points to obj bar(); //inside bar, this points to the global object (or undefined in strict mode)
In my example the function
this.renderMovie()
is passed as a parameter. So inside ListView it will be a "local variable". It will be called "detached" from any object, like thebar()
example before, sothis
will not be what I expect.It's possible to force the binding of
this
.
There are 3 possible solutions to my problem, and I like the 3rd one the best:
On the code, replace the referece to {this.renderMovie}
whith {this.renderMovie.bind(this)}
render() {
console.log('this', this); //this is an instance of AwesomeProject
if (!this.state.loaded) {
return this.renderLoadingView();
}
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderMovie.bind(this)} //<-- change here
style={styles.listView}
/>
);
}
And of course, bind the right function:
constructor(props) {
super(props);
this.something = this.something.bind(this); // <-- Not this function
this.renderMovie = this.renderMovie.bind(this); // <-- This function!!
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
loaded: false,
};
}
I like this one the best. You just need to declare the involved function using the arrow syntax so this:
renderMovie(movie) {
//code here
}
becomes this:
renderMovie = (movie) => {
//code here
}
In addition to https://stackoverflow.com/a/36779517/6100821
<ListView
dataSource={this.state.dataSource}
renderRow={::this.renderMovie} //<-- cleaner, than bind
style={styles.listView}
/>
Be careful, it's just a proposal, not a standard!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With