How to access `this` inside a renderRow of a ListView?
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:
Option 1: Manually bind this on call
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}
/>
);
}
Option 2: Do the binding on the constructor
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,
};
}
Option 3: use the arrow syntax and let them do the job
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
Option 4: use bind operator
<ListView
dataSource={this.state.dataSource}
renderRow={::this.renderMovie} //<-- cleaner, than bind
style={styles.listView}
/>
Be careful, it's just a proposal, not a standard!